Keep in line with the parent class.
+++ /dev/null
-/* GDK - The GIMP Drawing Kit
- * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GTK+ Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-#include "config.h"
-
-#include "gdkx11dnd.h"
-
-#include "gdk-private.h"
-#include "gdkasync.h"
-#include "gdkclipboardprivate.h"
-#include "gdkclipboard-x11.h"
-#include "gdkdeviceprivate.h"
-#include "gdkdisplay-x11.h"
-#include "gdkdragprivate.h"
-#include "gdkinternals.h"
-#include "gdkintl.h"
-#include "gdkproperty.h"
-#include "gdkprivate-x11.h"
-#include "gdkscreen-x11.h"
-#include "gdkselectioninputstream-x11.h"
-#include "gdkselectionoutputstream-x11.h"
-
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
-#include <X11/extensions/shape.h>
-#ifdef HAVE_XCOMPOSITE
-#include <X11/extensions/Xcomposite.h>
-#endif
-
-#include <string.h>
-
-typedef enum {
- GDK_DRAG_STATUS_DRAG,
- GDK_DRAG_STATUS_MOTION_WAIT,
- GDK_DRAG_STATUS_ACTION_WAIT,
- GDK_DRAG_STATUS_DROP
-} GtkDragStatus;
-
-/*
- * GdkDragProtocol:
- * @GDK_DRAG_PROTO_NONE: no protocol.
- * @GDK_DRAG_PROTO_XDND: The Xdnd protocol.
- * @GDK_DRAG_PROTO_ROOTWIN: An extension to the Xdnd protocol for
- * unclaimed root window drops.
- *
- * Used in #GdkDrag to indicate the protocol according to
- * which DND is done.
- */
-typedef enum
-{
- GDK_DRAG_PROTO_NONE = 0,
- GDK_DRAG_PROTO_XDND,
- GDK_DRAG_PROTO_ROOTWIN
-} GdkDragProtocol;
-
-typedef struct {
- guint32 xid;
- gint x, y, width, height;
- gboolean mapped;
- gboolean shape_selected;
- gboolean shape_valid;
- cairo_region_t *shape;
-} GdkCacheChild;
-
-struct _GdkSurfaceCache {
- GList *children;
- GHashTable *child_hash;
- guint old_event_mask;
- GdkDisplay *display;
- gint ref_count;
-};
-
-
-struct _GdkX11Drag
-{
- GdkDrag drag;
-
- GdkDragProtocol protocol;
-
- gint start_x; /* Where the drag started */
- gint start_y;
- guint16 last_x; /* Coordinates from last event */
- guint16 last_y;
- gulong timestamp; /* Timestamp we claimed the DND selection with */
- GdkDragAction xdnd_actions; /* What is currently set in XdndActionList */
- guint version; /* Xdnd protocol version */
-
- GdkSurfaceCache *cache;
-
- GdkSurface *drag_surface;
-
- GdkSurface *ipc_surface;
- GdkCursor *cursor;
- GdkSeat *grab_seat;
- GdkDragAction actions;
- GdkDragAction current_action;
-
- gint hot_x;
- gint hot_y;
-
- Window dest_xid; /* The last window we looked up */
- Window proxy_xid; /* The proxy window for dest_xid (or dest_xid if no proxying happens) */
- Window drop_xid; /* The (non-proxied) window that is receiving drops */
- guint xdnd_targets_set : 1; /* Whether we've already set XdndTypeList */
- guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
- guint drag_status : 4; /* current status of drag */
- guint drop_failed : 1; /* Whether the drop was unsuccessful */
-};
-
-struct _GdkX11DragClass
-{
- GdkDragClass parent_class;
-};
-
-typedef struct {
- gint keysym;
- gint modifiers;
-} GrabKey;
-
-static GrabKey grab_keys[] = {
- { XK_Escape, 0 },
- { XK_space, 0 },
- { XK_KP_Space, 0 },
- { XK_Return, 0 },
- { XK_KP_Enter, 0 },
- { XK_Up, 0 },
- { XK_Up, Mod1Mask },
- { XK_Down, 0 },
- { XK_Down, Mod1Mask },
- { XK_Left, 0 },
- { XK_Left, Mod1Mask },
- { XK_Right, 0 },
- { XK_Right, Mod1Mask },
- { XK_KP_Up, 0 },
- { XK_KP_Up, Mod1Mask },
- { XK_KP_Down, 0 },
- { XK_KP_Down, Mod1Mask },
- { XK_KP_Left, 0 },
- { XK_KP_Left, Mod1Mask },
- { XK_KP_Right, 0 },
- { XK_KP_Right, Mod1Mask }
-};
-
-/* Forward declarations */
-
-static GdkSurfaceCache *gdk_surface_cache_ref (GdkSurfaceCache *cache);
-static void gdk_surface_cache_unref (GdkSurfaceCache *cache);
-
-gboolean gdk_x11_drag_handle_event (GdkDrag *drag,
- const GdkEvent *event);
-
-static GList *drags;
-static GSList *window_caches;
-
-G_DEFINE_TYPE (GdkX11Drag, gdk_x11_drag, GDK_TYPE_DRAG)
-
-static void
-gdk_x11_drag_init (GdkX11Drag *drag)
-{
- drags = g_list_prepend (drags, drag);
-}
-
-static void gdk_x11_drag_finalize (GObject *object);
-static Window gdk_x11_drag_find_surface (GdkDrag *drag,
- GdkSurface *drag_surface,
- gint x_root,
- gint y_root,
- GdkDragProtocol *protocol);
-static gboolean gdk_x11_drag_drag_motion (GdkDrag *drag,
- Window proxy_xid,
- GdkDragProtocol protocol,
- gint x_root,
- gint y_root,
- GdkDragAction suggested_action,
- GdkDragAction possible_actions,
- guint32 time);
-static void gdk_x11_drag_drop (GdkDrag *drag,
- guint32 time_);
-static GdkSurface * gdk_x11_drag_get_drag_surface (GdkDrag *drag);
-static void gdk_x11_drag_set_hotspot (GdkDrag *drag,
- gint hot_x,
- gint hot_y);
-static void gdk_x11_drag_drop_done (GdkDrag *drag,
- gboolean success);
-static void gdk_x11_drag_set_cursor (GdkDrag *drag,
- GdkCursor *cursor);
-static void gdk_x11_drag_cancel (GdkDrag *drag,
- GdkDragCancelReason reason);
-static void gdk_x11_drag_drop_performed (GdkDrag *drag,
- guint32 time);
-
-static void
-gdk_x11_drag_class_init (GdkX11DragClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GdkDragClass *drag_class = GDK_DRAG_CLASS (klass);
-
- object_class->finalize = gdk_x11_drag_finalize;
-
- drag_class->get_drag_surface = gdk_x11_drag_get_drag_surface;
- drag_class->set_hotspot = gdk_x11_drag_set_hotspot;
- drag_class->drop_done = gdk_x11_drag_drop_done;
- drag_class->set_cursor = gdk_x11_drag_set_cursor;
- drag_class->cancel = gdk_x11_drag_cancel;
- drag_class->drop_performed = gdk_x11_drag_drop_performed;
- drag_class->handle_event = gdk_x11_drag_handle_event;
-}
-
-static void
-gdk_x11_drag_finalize (GObject *object)
-{
- GdkDrag *drag = GDK_DRAG (object);
- GdkX11Drag *x11_drag = GDK_X11_DRAG (object);
- GdkSurface *drag_surface, *ipc_surface;
-
- if (x11_drag->cache)
- gdk_surface_cache_unref (x11_drag->cache);
-
- drags = g_list_remove (drags, drag);
-
- drag_surface = x11_drag->drag_surface;
- ipc_surface = x11_drag->ipc_surface;
-
- G_OBJECT_CLASS (gdk_x11_drag_parent_class)->finalize (object);
-
- if (drag_surface)
- gdk_surface_destroy (drag_surface);
- if (ipc_surface)
- gdk_surface_destroy (ipc_surface);
-}
-
-/* Drag Contexts */
-
-GdkDrag *
-gdk_x11_drag_find (GdkDisplay *display,
- Window source_xid,
- Window dest_xid)
-{
- GList *tmp_list;
- GdkDrag *drag;
- GdkX11Drag *drag_x11;
- Window drag_dest_xid;
- GdkSurface *surface;
- Window surface_xid;
-
- for (tmp_list = drags; tmp_list; tmp_list = tmp_list->next)
- {
- drag = (GdkDrag *)tmp_list->data;
- drag_x11 = (GdkX11Drag *)drag;
-
- if (gdk_drag_get_display (drag) != display)
- continue;
-
- g_object_get (drag, "surface", &surface, NULL);
- surface_xid = surface ? GDK_SURFACE_XID (surface) : None;
- g_object_unref (surface);
-
- drag_dest_xid = drag_x11->proxy_xid
- ? (drag_x11->drop_xid
- ? drag_x11->drop_xid
- : drag_x11->proxy_xid)
- : None;
-
- if (((source_xid == None) || (surface && (surface_xid == source_xid))) &&
- ((dest_xid == None) || (drag_dest_xid == dest_xid)))
- return drag;
- }
-
- return NULL;
-}
-
-static void
-precache_target_list (GdkDrag *drag)
-{
- GdkContentFormats *formats = gdk_drag_get_formats (drag);
- const char * const *atoms;
- gsize n_atoms;
-
- atoms = gdk_content_formats_get_mime_types (formats, &n_atoms);
-
- _gdk_x11_precache_atoms (gdk_drag_get_display (drag),
- (const gchar **) atoms,
- n_atoms);
-}
-
-/* Utility functions */
-
-static void
-free_cache_child (GdkCacheChild *child,
- GdkDisplay *display)
-{
- if (child->shape)
- cairo_region_destroy (child->shape);
-
- if (child->shape_selected && display)
- {
- GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
-
- XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
- }
-
- g_free (child);
-}
-
-static void
-gdk_surface_cache_add (GdkSurfaceCache *cache,
- guint32 xid,
- gint x,
- gint y,
- gint width,
- gint height,
- gboolean mapped)
-{
- GdkCacheChild *child = g_new (GdkCacheChild, 1);
-
- child->xid = xid;
- child->x = x;
- child->y = y;
- child->width = width;
- child->height = height;
- child->mapped = mapped;
- child->shape_selected = FALSE;
- child->shape_valid = FALSE;
- child->shape = NULL;
-
- cache->children = g_list_prepend (cache->children, child);
- g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
- cache->children);
-}
-
-GdkFilterReturn
-gdk_surface_cache_shape_filter (const XEvent *xevent,
- GdkEvent *event,
- gpointer data)
-{
- GdkSurfaceCache *cache = data;
-
- GdkX11Display *display = GDK_X11_DISPLAY (cache->display);
-
- if (display->have_shapes &&
- xevent->type == display->shape_event_base + ShapeNotify)
- {
- XShapeEvent *xse = (XShapeEvent*)xevent;
- GList *node;
-
- node = g_hash_table_lookup (cache->child_hash,
- GUINT_TO_POINTER (xse->window));
- if (node)
- {
- GdkCacheChild *child = node->data;
- child->shape_valid = FALSE;
- if (child->shape)
- {
- cairo_region_destroy (child->shape);
- child->shape = NULL;
- }
- }
-
- return GDK_FILTER_REMOVE;
- }
-
- return GDK_FILTER_CONTINUE;
-}
-
-GdkFilterReturn
-gdk_surface_cache_filter (const XEvent *xevent,
- GdkEvent *event,
- gpointer data)
-{
- GdkSurfaceCache *cache = data;
-
- switch (xevent->type)
- {
- case CirculateNotify:
- break;
- case ConfigureNotify:
- {
- const XConfigureEvent *xce = &xevent->xconfigure;
- GList *node;
-
- node = g_hash_table_lookup (cache->child_hash,
- GUINT_TO_POINTER (xce->window));
- if (node)
- {
- GdkCacheChild *child = node->data;
- child->x = xce->x;
- child->y = xce->y;
- child->width = xce->width;
- child->height = xce->height;
- if (xce->above == None && (node->next))
- {
- GList *last = g_list_last (cache->children);
- cache->children = g_list_remove_link (cache->children, node);
- last->next = node;
- node->next = NULL;
- node->prev = last;
- }
- else
- {
- GList *above_node = g_hash_table_lookup (cache->child_hash,
- GUINT_TO_POINTER (xce->above));
- if (above_node && node->next != above_node)
- {
- /* Put the window above (before in the list) above_node */
- cache->children = g_list_remove_link (cache->children, node);
- node->prev = above_node->prev;
- if (node->prev)
- node->prev->next = node;
- else
- cache->children = node;
- node->next = above_node;
- above_node->prev = node;
- }
- }
- }
- break;
- }
- case CreateNotify:
- {
- const XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
-
- if (!g_hash_table_lookup (cache->child_hash,
- GUINT_TO_POINTER (xcwe->window)))
- gdk_surface_cache_add (cache, xcwe->window,
- xcwe->x, xcwe->y, xcwe->width, xcwe->height,
- FALSE);
- break;
- }
- case DestroyNotify:
- {
- const XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
- GList *node;
-
- node = g_hash_table_lookup (cache->child_hash,
- GUINT_TO_POINTER (xdwe->window));
- if (node)
- {
- GdkCacheChild *child = node->data;
-
- g_hash_table_remove (cache->child_hash,
- GUINT_TO_POINTER (xdwe->window));
- cache->children = g_list_remove_link (cache->children, node);
- /* window is destroyed, no need to disable ShapeNotify */
- free_cache_child (child, NULL);
- g_list_free_1 (node);
- }
- break;
- }
- case MapNotify:
- {
- const XMapEvent *xme = &xevent->xmap;
- GList *node;
-
- node = g_hash_table_lookup (cache->child_hash,
- GUINT_TO_POINTER (xme->window));
- if (node)
- {
- GdkCacheChild *child = node->data;
- child->mapped = TRUE;
- }
- break;
- }
- case ReparentNotify:
- break;
- case UnmapNotify:
- {
- const XMapEvent *xume = &xevent->xmap;
- GList *node;
-
- node = g_hash_table_lookup (cache->child_hash,
- GUINT_TO_POINTER (xume->window));
- if (node)
- {
- GdkCacheChild *child = node->data;
- child->mapped = FALSE;
- }
- break;
- }
- default:
- return GDK_FILTER_CONTINUE;
- }
- return GDK_FILTER_REMOVE;
-}
-
-static GdkSurfaceCache *
-gdk_surface_cache_new (GdkDisplay *display)
-{
- XWindowAttributes xwa;
- GdkX11Screen *screen = GDK_X11_DISPLAY (display)->screen;
- Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
- Window xroot_window = GDK_DISPLAY_XROOTWIN (display);
- GdkChildInfoX11 *children;
- guint nchildren, i;
-#ifdef HAVE_XCOMPOSITE
- Window cow;
-#endif
-
- GdkSurfaceCache *result = g_new (GdkSurfaceCache, 1);
-
- result->children = NULL;
- result->child_hash = g_hash_table_new (g_direct_hash, NULL);
- result->display = display;
- result->ref_count = 1;
-
- XGetWindowAttributes (xdisplay, xroot_window, &xwa);
- result->old_event_mask = xwa.your_event_mask;
-
- if (G_UNLIKELY (!GDK_X11_DISPLAY (display)->trusted_client))
- {
- GList *toplevel_windows, *list;
- GdkSurface *surface;
- GdkSurfaceImplX11 *impl;
- gint x, y, width, height;
-
- toplevel_windows = gdk_x11_display_get_toplevel_windows (display);
- for (list = toplevel_windows; list; list = list->next)
- {
- surface = GDK_SURFACE (list->data);
- impl = GDK_SURFACE_IMPL_X11 (surface->impl);
- gdk_surface_get_geometry (surface, &x, &y, &width, &height);
- gdk_surface_cache_add (result, GDK_SURFACE_XID (surface),
- x * impl->surface_scale, y * impl->surface_scale,
- width * impl->surface_scale,
- height * impl->surface_scale,
- gdk_surface_is_visible (surface));
- }
- return result;
- }
-
- XSelectInput (xdisplay, xroot_window, result->old_event_mask | SubstructureNotifyMask);
-
- if (!_gdk_x11_get_window_child_info (display,
- xroot_window,
- FALSE, NULL,
- &children, &nchildren))
- return result;
-
- for (i = 0; i < nchildren ; i++)
- {
- gdk_surface_cache_add (result, children[i].window,
- children[i].x, children[i].y, children[i].width, children[i].height,
- children[i].is_mapped);
- }
-
- g_free (children);
-
-#ifdef HAVE_XCOMPOSITE
- /*
- * Add the composite overlay window to the cache, as this can be a reasonable
- * Xdnd proxy as well.
- * This is only done when the screen is composited in order to avoid mapping
- * the COW. We assume that the CM is using the COW (which is true for pretty
- * much any CM currently in use).
- */
- if (gdk_display_is_composited (display))
- {
- cow = XCompositeGetOverlayWindow (xdisplay, xroot_window);
- gdk_surface_cache_add (result, cow, 0, 0,
- WidthOfScreen (GDK_X11_SCREEN (screen)->xscreen),
- HeightOfScreen (GDK_X11_SCREEN (screen)->xscreen),
- TRUE);
- XCompositeReleaseOverlayWindow (xdisplay, xroot_window);
- }
-#endif
-
- return result;
-}
-
-static void
-gdk_surface_cache_destroy (GdkSurfaceCache *cache)
-{
- XSelectInput (GDK_DISPLAY_XDISPLAY (cache->display),
- GDK_DISPLAY_XROOTWIN (cache->display),
- cache->old_event_mask);
-
- gdk_x11_display_error_trap_push (cache->display);
- g_list_foreach (cache->children, (GFunc)free_cache_child, cache->display);
- gdk_x11_display_error_trap_pop_ignored (cache->display);
-
- g_list_free (cache->children);
- g_hash_table_destroy (cache->child_hash);
-
- g_free (cache);
-}
-
-static GdkSurfaceCache *
-gdk_surface_cache_ref (GdkSurfaceCache *cache)
-{
- cache->ref_count += 1;
-
- return cache;
-}
-
-static void
-gdk_surface_cache_unref (GdkSurfaceCache *cache)
-{
- g_assert (cache->ref_count > 0);
-
- cache->ref_count -= 1;
-
- if (cache->ref_count == 0)
- {
- window_caches = g_slist_remove (window_caches, cache);
- gdk_surface_cache_destroy (cache);
- }
-}
-
-GdkSurfaceCache *
-gdk_surface_cache_get (GdkDisplay *display)
-{
- GSList *list;
- GdkSurfaceCache *cache;
-
- for (list = window_caches; list; list = list->next)
- {
- cache = list->data;
- if (cache->display == display)
- return gdk_surface_cache_ref (cache);
- }
-
- cache = gdk_surface_cache_new (display);
-
- window_caches = g_slist_prepend (window_caches, cache);
-
- return cache;
-}
-
-static gboolean
-is_pointer_within_shape (GdkDisplay *display,
- GdkCacheChild *child,
- gint x_pos,
- gint y_pos)
-{
- if (!child->shape_selected)
- {
- GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
-
- XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
- child->shape_selected = TRUE;
- }
- if (!child->shape_valid)
- {
- GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
- cairo_region_t *input_shape;
-
- child->shape = NULL;
- if (gdk_display_supports_shapes (display))
- child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
- child->xid, 1, ShapeBounding);
-#ifdef ShapeInput
- input_shape = NULL;
- if (gdk_display_supports_input_shapes (display))
- input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
- child->xid, 1, ShapeInput);
-
- if (child->shape && input_shape)
- {
- cairo_region_intersect (child->shape, input_shape);
- cairo_region_destroy (input_shape);
- }
- else if (input_shape)
- {
- child->shape = input_shape;
- }
-#endif
-
- child->shape_valid = TRUE;
- }
-
- return child->shape == NULL ||
- cairo_region_contains_point (child->shape, x_pos, y_pos);
-}
-
-static Window
-get_client_window_at_coords_recurse (GdkDisplay *display,
- Window win,
- gboolean is_toplevel,
- gint x,
- gint y)
-{
- GdkChildInfoX11 *children;
- unsigned int nchildren;
- int i;
- gboolean found_child = FALSE;
- GdkChildInfoX11 child = { 0, };
- gboolean has_wm_state = FALSE;
-
- if (!_gdk_x11_get_window_child_info (display, win, TRUE,
- is_toplevel? &has_wm_state : NULL,
- &children, &nchildren))
- return None;
-
- if (has_wm_state)
- {
- g_free (children);
-
- return win;
- }
-
- for (i = nchildren - 1; (i >= 0) && !found_child; i--)
- {
- GdkChildInfoX11 *cur_child = &children[i];
-
- if ((cur_child->is_mapped) && (cur_child->window_class == InputOutput) &&
- (x >= cur_child->x) && (x < cur_child->x + cur_child->width) &&
- (y >= cur_child->y) && (y < cur_child->y + cur_child->height))
- {
- x -= cur_child->x;
- y -= cur_child->y;
- child = *cur_child;
- found_child = TRUE;
- }
- }
-
- g_free (children);
-
- if (found_child)
- {
- if (child.has_wm_state)
- return child.window;
- else
- return get_client_window_at_coords_recurse (display, child.window, FALSE, x, y);
- }
- else
- return None;
-}
-
-static Window
-get_client_window_at_coords (GdkSurfaceCache *cache,
- Window ignore,
- gint x_root,
- gint y_root)
-{
- GList *tmp_list;
- Window retval = None;
- GdkDisplay *display;
-
- display = cache->display;
-
- gdk_x11_display_error_trap_push (display);
-
- tmp_list = cache->children;
-
- while (tmp_list && !retval)
- {
- GdkCacheChild *child = tmp_list->data;
-
- if ((child->xid != ignore) && (child->mapped))
- {
- if ((x_root >= child->x) && (x_root < child->x + child->width) &&
- (y_root >= child->y) && (y_root < child->y + child->height))
- {
- if (!is_pointer_within_shape (display, child,
- x_root - child->x,
- y_root - child->y))
- {
- tmp_list = tmp_list->next;
- continue;
- }
-
- retval = get_client_window_at_coords_recurse (display,
- child->xid, TRUE,
- x_root - child->x,
- y_root - child->y);
- if (!retval)
- retval = child->xid;
- }
- }
- tmp_list = tmp_list->next;
- }
-
- gdk_x11_display_error_trap_pop_ignored (display);
-
- if (retval)
- return retval;
- else
- return GDK_DISPLAY_XROOTWIN (display);
-}
-
-/*************************************************************
- ***************************** XDND **************************
- *************************************************************/
-
-/* Utility functions */
-
-static struct {
- const gchar *name;
- GdkDragAction action;
-} xdnd_actions_table[] = {
- { "XdndActionCopy", GDK_ACTION_COPY },
- { "XdndActionMove", GDK_ACTION_MOVE },
- { "XdndActionLink", GDK_ACTION_LINK },
- { "XdndActionAsk", GDK_ACTION_ASK },
- { "XdndActionPrivate", GDK_ACTION_COPY },
- };
-
-static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
-
-static GdkDragAction
-xdnd_action_from_atom (GdkDisplay *display,
- Atom xatom)
-{
- const char *name;
- gint i;
-
- if (xatom == None)
- return 0;
-
- name = gdk_x11_get_xatom_name_for_display (display, xatom);
-
- for (i = 0; i < xdnd_n_actions; i++)
- if (g_str_equal (name, xdnd_actions_table[i].name))
- return xdnd_actions_table[i].action;
-
- return 0;
-}
-
-static Atom
-xdnd_action_to_atom (GdkDisplay *display,
- GdkDragAction action)
-{
- gint i;
-
- for (i = 0; i < xdnd_n_actions; i++)
- if (action == xdnd_actions_table[i].action)
- return gdk_x11_get_xatom_by_name_for_display (display, xdnd_actions_table[i].name);
-
- return None;
-}
-
-/* Source side */
-
-void
-gdk_x11_drag_handle_status (GdkDisplay *display,
- const XEvent *xevent)
-{
- guint32 dest_surface = xevent->xclient.data.l[0];
- guint32 flags = xevent->xclient.data.l[1];
- Atom action = xevent->xclient.data.l[4];
- GdkDrag *drag;
-
- drag = gdk_x11_drag_find (display, xevent->xclient.window, dest_surface);
-
- GDK_DISPLAY_NOTE (display, DND,
- g_message ("XdndStatus: dest_surface: %#x action: %ld",
- dest_surface, action));
-
- if (drag)
- {
- GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
- if (drag_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
- drag_x11->drag_status = GDK_DRAG_STATUS_DRAG;
-
- if (!(action != 0) != !(flags & 1))
- {
- GDK_DISPLAY_NOTE (display, DND,
- g_warning ("Received status event with flags not corresponding to action!"));
- action = 0;
- }
-
- gdk_drag_set_selected_action (drag, xdnd_action_from_atom (display, action));
- drag_x11->current_action = action;
- }
-}
-
-void
-gdk_x11_drag_handle_finished (GdkDisplay *display,
- const XEvent *xevent)
-{
- guint32 dest_surface = xevent->xclient.data.l[0];
- GdkDrag *drag;
- GdkX11Drag *drag_x11;
-
- drag = gdk_x11_drag_find (display, xevent->xclient.window, dest_surface);
-
- GDK_DISPLAY_NOTE (display, DND,
- g_message ("XdndFinished: dest_surface: %#x", dest_surface));
-
- if (drag)
- {
- g_object_ref (drag);
-
- drag_x11 = GDK_X11_DRAG (drag);
- if (drag_x11->version == 5)
- drag_x11->drop_failed = xevent->xclient.data.l[1] == 0;
-
- g_signal_emit_by_name (drag, "dnd-finished");
- gdk_drag_drop_done (drag, !drag_x11->drop_failed);
-
- g_object_unref (drag);
- }
-}
-
-static void
-xdnd_set_targets (GdkX11Drag *drag_x11)
-{
- GdkDrag *drag = GDK_DRAG (drag_x11);
- Atom *atomlist;
- const char * const *atoms;
- gsize i, n_atoms;
- GdkDisplay *display = gdk_drag_get_display (drag);
-
- atoms = gdk_content_formats_get_mime_types (gdk_drag_get_formats (drag), &n_atoms);
- atomlist = g_new (Atom, n_atoms);
- for (i = 0; i < n_atoms; i++)
- atomlist[i] = gdk_x11_get_xatom_by_name_for_display (display, atoms[i]);
-
- XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
- GDK_SURFACE_XID (drag_x11->ipc_surface),
- gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
- XA_ATOM, 32, PropModeReplace,
- (guchar *)atomlist, n_atoms);
-
- g_free (atomlist);
-
- drag_x11->xdnd_targets_set = 1;
-}
-
-static void
-xdnd_set_actions (GdkX11Drag *drag_x11)
-{
- GdkDrag *drag = GDK_DRAG (drag_x11);
- Atom *atomlist;
- gint i;
- gint n_atoms;
- guint actions;
- GdkDisplay *display = gdk_drag_get_display (drag);
-
- actions = gdk_drag_get_actions (drag);
- n_atoms = 0;
- for (i = 0; i < xdnd_n_actions; i++)
- {
- if (actions & xdnd_actions_table[i].action)
- {
- actions &= ~xdnd_actions_table[i].action;
- n_atoms++;
- }
- }
-
- atomlist = g_new (Atom, n_atoms);
-
- actions = gdk_drag_get_actions (drag);
- n_atoms = 0;
- for (i = 0; i < xdnd_n_actions; i++)
- {
- if (actions & xdnd_actions_table[i].action)
- {
- actions &= ~xdnd_actions_table[i].action;
- atomlist[n_atoms] = gdk_x11_get_xatom_by_name_for_display (display, xdnd_actions_table[i].name);
- n_atoms++;
- }
- }
-
- XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
- GDK_SURFACE_XID (drag_x11->ipc_surface),
- gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
- XA_ATOM, 32, PropModeReplace,
- (guchar *)atomlist, n_atoms);
-
- g_free (atomlist);
-
- drag_x11->xdnd_actions = gdk_drag_get_actions (drag);
-}
-
-static void
-send_client_message_async_cb (Window window,
- gboolean success,
- gpointer data)
-{
- GdkX11Drag *drag_x11 = data;
- GdkDrag *drag = data;
-
- GDK_DISPLAY_NOTE (gdk_drag_get_display (drag), DND,
- g_message ("Got async callback for #%lx, success = %d",
- window, success));
-
- /* On failure, we immediately continue with the protocol
- * so we don't end up blocking for a timeout
- */
- if (!success &&
- window == drag_x11->proxy_xid)
- {
- drag_x11->proxy_xid = None;
- gdk_drag_set_selected_action (drag, 0);
- drag_x11->current_action = 0;
- drag_x11->drag_status = GDK_DRAG_STATUS_DRAG;
- }
-
- g_object_unref (drag);
-}
-
-static void
-send_client_message_async (GdkDrag *drag,
- Window window,
- glong event_mask,
- XClientMessageEvent *event_send)
-{
- GdkDisplay *display = gdk_drag_get_display (drag);
-
- g_object_ref (drag);
-
- _gdk_x11_send_client_message_async (display, window,
- FALSE, event_mask, event_send,
- send_client_message_async_cb, drag);
-}
-
-static void
-xdnd_send_xevent (GdkX11Drag *drag_x11,
- XEvent *event_send)
-{
- GdkDrag *drag = GDK_DRAG (drag_x11);
- GdkDisplay *display = gdk_drag_get_display (drag);
- GdkSurface *surface;
- glong event_mask;
-
- g_assert (event_send->xany.type == ClientMessage);
-
- /* We short-circuit messages to ourselves */
- surface = gdk_x11_surface_lookup_for_display (display, drag_x11->proxy_xid);
- if (surface)
- {
- if (gdk_x11_drop_filter (surface, event_send))
- return;
- }
-
- if (_gdk_x11_display_is_root_window (display, drag_x11->proxy_xid))
- event_mask = ButtonPressMask;
- else
- event_mask = 0;
-
- send_client_message_async (drag, drag_x11->proxy_xid, event_mask,
- &event_send->xclient);
-}
-
-static void
-xdnd_send_enter (GdkX11Drag *drag_x11)
-{
- GdkDrag *drag = GDK_DRAG (drag_x11);
- GdkDisplay *display = gdk_drag_get_display (drag);
- const char * const *atoms;
- gsize i, n_atoms;
- XEvent xev;
-
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndEnter");
- xev.xclient.format = 32;
- xev.xclient.window = drag_x11->drop_xid
- ? drag_x11->drop_xid
- : drag_x11->proxy_xid;
- xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
- xev.xclient.data.l[1] = (drag_x11->version << 24); /* version */
- xev.xclient.data.l[2] = 0;
- xev.xclient.data.l[3] = 0;
- xev.xclient.data.l[4] = 0;
-
- GDK_DISPLAY_NOTE (display, DND,
- g_message ("Sending enter source window %#lx XDND protocol version %d\n",
- GDK_SURFACE_XID (drag_x11->ipc_surface), drag_x11->version));
- atoms = gdk_content_formats_get_mime_types (gdk_drag_get_formats (drag), &n_atoms);
-
- if (n_atoms > 3)
- {
- if (!drag_x11->xdnd_targets_set)
- xdnd_set_targets (drag_x11);
- xev.xclient.data.l[1] |= 1;
- }
- else
- {
- for (i = 0; i < n_atoms; i++)
- {
- xev.xclient.data.l[i + 2] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
- }
- }
-
- xdnd_send_xevent (drag_x11, &xev);
-}
-
-static void
-xdnd_send_leave (GdkX11Drag *drag_x11)
-{
- GdkDrag *drag = GDK_DRAG (drag_x11);
- GdkDisplay *display = gdk_drag_get_display (drag);
- XEvent xev;
-
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndLeave");
- xev.xclient.format = 32;
- xev.xclient.window = drag_x11->drop_xid
- ? drag_x11->drop_xid
- : drag_x11->proxy_xid;
- xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
- xev.xclient.data.l[1] = 0;
- xev.xclient.data.l[2] = 0;
- xev.xclient.data.l[3] = 0;
- xev.xclient.data.l[4] = 0;
-
- xdnd_send_xevent (drag_x11, &xev);
-}
-
-static void
-xdnd_send_drop (GdkX11Drag *drag_x11,
- guint32 time)
-{
- GdkDrag *drag = GDK_DRAG (drag_x11);
- GdkDisplay *display = gdk_drag_get_display (drag);
- XEvent xev;
-
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndDrop");
- xev.xclient.format = 32;
- xev.xclient.window = drag_x11->drop_xid
- ? drag_x11->drop_xid
- : drag_x11->proxy_xid;
- xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
- xev.xclient.data.l[1] = 0;
- xev.xclient.data.l[2] = time;
- xev.xclient.data.l[3] = 0;
- xev.xclient.data.l[4] = 0;
-
- xdnd_send_xevent (drag_x11, &xev);
-}
-
-static void
-xdnd_send_motion (GdkX11Drag *drag_x11,
- gint x_root,
- gint y_root,
- GdkDragAction action,
- guint32 time)
-{
- GdkDrag *drag = GDK_DRAG (drag_x11);
- GdkDisplay *display = gdk_drag_get_display (drag);
- XEvent xev;
-
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndPosition");
- xev.xclient.format = 32;
- xev.xclient.window = drag_x11->drop_xid
- ? drag_x11->drop_xid
- : drag_x11->proxy_xid;
- xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
- xev.xclient.data.l[1] = 0;
- xev.xclient.data.l[2] = (x_root << 16) | y_root;
- xev.xclient.data.l[3] = time;
- xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
-
- xdnd_send_xevent (drag_x11, &xev);
- drag_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
-}
-
-static guint32
-xdnd_check_dest (GdkDisplay *display,
- Window win,
- guint *xdnd_version)
-{
- gboolean retval = FALSE;
- Atom type = None;
- int format;
- unsigned long nitems, after;
- guchar *data;
- Atom *version;
- Window *proxy_data;
- Window proxy;
- Atom xdnd_proxy_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndProxy");
- Atom xdnd_aware_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndAware");
-
- proxy = None;
-
- gdk_x11_display_error_trap_push (display);
- if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
- xdnd_proxy_atom, 0,
- 1, False, AnyPropertyType,
- &type, &format, &nitems, &after,
- &data) == Success)
- {
- if (type != None)
- {
- proxy_data = (Window *)data;
-
- if ((format == 32) && (nitems == 1))
- {
- proxy = *proxy_data;
- }
- else
- {
- GDK_DISPLAY_NOTE (display, DND,
- g_warning ("Invalid XdndProxy property on window %ld", win));
- }
-
- XFree (proxy_data);
- }
-
- if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
- xdnd_aware_atom, 0,
- 1, False, AnyPropertyType,
- &type, &format, &nitems, &after,
- &data) == Success) &&
- type != None)
- {
- version = (Atom *)data;
-
- if ((format == 32) && (nitems == 1))
- {
- if (*version >= 3)
- retval = TRUE;
- if (xdnd_version)
- *xdnd_version = *version;
- }
- else
- {
- GDK_DISPLAY_NOTE (display, DND,
- g_warning ("Invalid XdndAware property on window %ld", win));
- }
-
- XFree (version);
- }
- }
-
- gdk_x11_display_error_trap_pop_ignored (display);
-
- return retval ? (proxy ? proxy : win) : None;
-}
-
-/* Target side */
-
-static void
-base_precache_atoms (GdkDisplay *display)
-{
- GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
-
- if (!display_x11->base_dnd_atoms_precached)
- {
- static const char *const precache_atoms[] = {
- "WM_STATE",
- "XdndAware",
- "XdndProxy"
- };
-
- _gdk_x11_precache_atoms (display,
- precache_atoms, G_N_ELEMENTS (precache_atoms));
-
- display_x11->base_dnd_atoms_precached = TRUE;
- }
-}
-
-static void
-xdnd_precache_atoms (GdkDisplay *display)
-{
- GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
-
- if (!display_x11->xdnd_atoms_precached)
- {
- static const gchar *const precache_atoms[] = {
- "XdndActionAsk",
- "XdndActionCopy",
- "XdndActionLink",
- "XdndActionList",
- "XdndActionMove",
- "XdndActionPrivate",
- "XdndDrop",
- "XdndEnter",
- "XdndFinished",
- "XdndLeave",
- "XdndPosition",
- "XdndSelection",
- "XdndStatus",
- "XdndTypeList"
- };
-
- _gdk_x11_precache_atoms (display,
- precache_atoms, G_N_ELEMENTS (precache_atoms));
-
- display_x11->xdnd_atoms_precached = TRUE;
- }
-}
-
-/* Source side */
-
-static void
-gdk_drag_do_leave (GdkX11Drag *drag_x11,
- guint32 time)
-{
- if (drag_x11->proxy_xid)
- {
- switch (drag_x11->protocol)
- {
- case GDK_DRAG_PROTO_XDND:
- xdnd_send_leave (drag_x11);
- break;
- case GDK_DRAG_PROTO_ROOTWIN:
- case GDK_DRAG_PROTO_NONE:
- default:
- break;
- }
-
- drag_x11->proxy_xid = None;
- }
-}
-
-static GdkSurface *
-create_drag_surface (GdkDisplay *display)
-{
- GdkSurface *surface;
-
- surface = gdk_surface_new_popup (display, &(GdkRectangle) { 0, 0, 100, 100 });
-
- gdk_surface_set_type_hint (surface, GDK_SURFACE_TYPE_HINT_DND);
-
- return surface;
-}
-
-static Window
-_gdk_x11_display_get_drag_protocol (GdkDisplay *display,
- Window xid,
- GdkDragProtocol *protocol,
- guint *version)
-
-{
- GdkSurface *surface;
- Window retval;
-
- base_precache_atoms (display);
-
- /* Check for a local drag */
- surface = gdk_x11_surface_lookup_for_display (display, xid);
- if (surface)
- {
- if (g_object_get_data (G_OBJECT (surface), "gdk-dnd-registered") != NULL)
- {
- *protocol = GDK_DRAG_PROTO_XDND;
- *version = 5;
- xdnd_precache_atoms (display);
- GDK_DISPLAY_NOTE (display, DND, g_message ("Entering local Xdnd window %#x\n", (guint) xid));
- return xid;
- }
- else if (_gdk_x11_display_is_root_window (display, xid))
- {
- *protocol = GDK_DRAG_PROTO_ROOTWIN;
- GDK_DISPLAY_NOTE (display, DND, g_message ("Entering root window\n"));
- return xid;
- }
- }
- else if ((retval = xdnd_check_dest (display, xid, version)))
- {
- *protocol = GDK_DRAG_PROTO_XDND;
- xdnd_precache_atoms (display);
- GDK_DISPLAY_NOTE (display, DND, g_message ("Entering Xdnd window %#x\n", (guint) xid));
- return retval;
- }
- else
- {
- /* Check if this is a root window */
- gboolean rootwin = FALSE;
-
- if (_gdk_x11_display_is_root_window (display, (Window) xid))
- rootwin = TRUE;
-
- if (rootwin)
- {
- GDK_DISPLAY_NOTE (display, DND, g_message ("Entering root window\n"));
- *protocol = GDK_DRAG_PROTO_ROOTWIN;
- return xid;
- }
- }
-
- *protocol = GDK_DRAG_PROTO_NONE;
-
- return 0; /* a.k.a. None */
-}
-
-static GdkSurfaceCache *
-drag_find_window_cache (GdkX11Drag *drag_x11,
- GdkDisplay *display)
-{
- if (!drag_x11->cache)
- drag_x11->cache = gdk_surface_cache_get (display);
-
- return drag_x11->cache;
-}
-
-static Window
-gdk_x11_drag_find_surface (GdkDrag *drag,
- GdkSurface *drag_surface,
- gint x_root,
- gint y_root,
- GdkDragProtocol *protocol)
-{
- GdkX11Screen *screen_x11;
- GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
- GdkSurfaceCache *window_cache;
- GdkDisplay *display;
- Window dest;
- Window proxy;
-
- display = gdk_drag_get_display (drag);
- screen_x11 = GDK_X11_SCREEN(GDK_X11_DISPLAY (display)->screen);
-
- window_cache = drag_find_window_cache (drag_x11, display);
-
- dest = get_client_window_at_coords (window_cache,
- drag_surface && GDK_SURFACE_IS_X11 (drag_surface) ?
- GDK_SURFACE_XID (drag_surface) : None,
- x_root * screen_x11->surface_scale,
- y_root * screen_x11->surface_scale);
-
- if (drag_x11->dest_xid != dest)
- {
- drag_x11->dest_xid = dest;
-
- /* Check if new destination accepts drags, and which protocol */
-
- /* There is some ugliness here. We actually need to pass
- * _three_ pieces of information to drag_motion - dest_surface,
- * protocol, and the XID of the unproxied window. The first
- * two are passed explicitly, the third implicitly through
- * protocol->dest_xid.
- */
- proxy = _gdk_x11_display_get_drag_protocol (display,
- dest,
- protocol,
- &drag_x11->version);
- }
- else
- {
- proxy = dest;
- *protocol = drag_x11->protocol;
- }
-
- return proxy;
-}
-
-static void
-move_drag_surface (GdkDrag *drag,
- guint x_root,
- guint y_root)
-{
- GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
-
- gdk_surface_move (drag_x11->drag_surface,
- x_root - drag_x11->hot_x,
- y_root - drag_x11->hot_y);
- gdk_surface_raise (drag_x11->drag_surface);
-}
-
-static gboolean
-gdk_x11_drag_drag_motion (GdkDrag *drag,
- Window proxy_xid,
- GdkDragProtocol protocol,
- gint x_root,
- gint y_root,
- GdkDragAction suggested_action,
- GdkDragAction possible_actions,
- guint32 time)
-{
- GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
-
- if (drag_x11->drag_surface)
- move_drag_surface (drag, x_root, y_root);
-
- gdk_drag_set_actions (drag, possible_actions);
-
- if (protocol == GDK_DRAG_PROTO_XDND && drag_x11->version == 0)
- {
- /* This ugly hack is necessary since GTK+ doesn't know about
- * the XDND protocol version, and in particular doesn't know
- * that gdk_drag_find_window() has the side-effect
- * of setting drag_x11->version, and therefore sometimes call
- * gdk_x11_drag_drag_motion() without a prior call to
- * gdk_drag_find_window(). This happens, e.g.
- * when GTK+ is proxying DND events to embedded windows.
- */
- if (proxy_xid)
- {
- GdkDisplay *display = gdk_drag_get_display (drag);
-
- xdnd_check_dest (display,
- proxy_xid,
- &drag_x11->version);
- }
- }
-
- /* When we have a Xdnd target, make sure our XdndActionList
- * matches the current actions;
- */
- if (protocol == GDK_DRAG_PROTO_XDND && drag_x11->xdnd_actions != gdk_drag_get_actions (drag))
- {
- if (proxy_xid)
- {
- GdkDisplay *display = gdk_drag_get_display (drag);
- GdkDrop *drop = GDK_X11_DISPLAY (display)->current_drop;
-
- if (drop && GDK_SURFACE_XID (gdk_drop_get_surface (drop)) == proxy_xid)
- gdk_x11_drop_read_actions (drop);
- else
- xdnd_set_actions (drag_x11);
- }
- }
-
- if (drag_x11->proxy_xid != proxy_xid)
- {
- /* Send a leave to the last destination */
- gdk_drag_do_leave (drag_x11, time);
- drag_x11->drag_status = GDK_DRAG_STATUS_DRAG;
-
- /* Check if new destination accepts drags, and which protocol */
-
- if (proxy_xid)
- {
- drag_x11->proxy_xid = proxy_xid;
- drag_x11->drop_xid = drag_x11->dest_xid;
- drag_x11->protocol = protocol;
-
- switch (protocol)
- {
- case GDK_DRAG_PROTO_XDND:
- xdnd_send_enter (drag_x11);
- break;
-
- case GDK_DRAG_PROTO_ROOTWIN:
- case GDK_DRAG_PROTO_NONE:
- default:
- break;
- }
- }
- else
- {
- drag_x11->proxy_xid = None;
- drag_x11->drop_xid = None;
- gdk_drag_set_selected_action (drag, 0);
- }
-
- /* Push a status event, to let the client know that
- * the drag changed
- */
- drag_x11->current_action = gdk_drag_get_selected_action (drag);
- }
-
- /* Send a drag-motion event */
-
- drag_x11->last_x = x_root;
- drag_x11->last_y = y_root;
-
- if (drag_x11->proxy_xid)
- {
- GdkDisplay *display = gdk_drag_get_display (drag);
- GdkX11Screen *screen_x11 = GDK_X11_SCREEN(GDK_X11_DISPLAY (display)->screen);
-
- if (drag_x11->drag_status == GDK_DRAG_STATUS_DRAG)
- {
- switch (drag_x11->protocol)
- {
- case GDK_DRAG_PROTO_XDND:
- xdnd_send_motion (drag_x11, x_root * screen_x11->surface_scale, y_root * screen_x11->surface_scale, suggested_action, time);
- break;
-
- case GDK_DRAG_PROTO_ROOTWIN:
- {
- GdkContentFormats *formats = gdk_drag_get_formats (drag);
- /* GTK+ traditionally has used application/x-rootwin-drop,
- * but the XDND spec specifies x-rootwindow-drop.
- */
- if (gdk_content_formats_contain_mime_type (formats, "application/x-rootwindow-drop") ||
- gdk_content_formats_contain_mime_type (formats, "application/x-rootwin-drop"))
- gdk_drag_set_selected_action (drag, suggested_action);
- else
- gdk_drag_set_selected_action (drag, 0);
-
- drag_x11->current_action = gdk_drag_get_selected_action (drag);
- }
- break;
- case GDK_DRAG_PROTO_NONE:
- g_warning ("Invalid drag protocol %u in gdk_x11_drag_drag_motion()", drag_x11->protocol);
- break;
- default:
- break;
- }
- }
- else
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-gdk_x11_drag_drop (GdkDrag *drag,
- guint32 time)
-{
- GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
-
- if (drag_x11->proxy_xid)
- {
- switch (drag_x11->protocol)
- {
- case GDK_DRAG_PROTO_XDND:
- xdnd_send_drop (drag_x11, time);
- break;
-
- case GDK_DRAG_PROTO_ROOTWIN:
- g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
- break;
- case GDK_DRAG_PROTO_NONE:
- g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
- break;
- default:
- g_warning ("Drag protocol %u is not valid in gdk_drag_drop()", drag_x11->protocol);
- break;
- }
- }
-}
-
-/* Destination side */
-
-void
-_gdk_x11_surface_register_dnd (GdkSurface *surface)
-{
- static const gulong xdnd_version = 5;
- GdkDisplay *display = gdk_surface_get_display (surface);
-
- g_return_if_fail (surface != NULL);
-
- base_precache_atoms (display);
-
- if (g_object_get_data (G_OBJECT (surface), "gdk-dnd-registered") != NULL)
- return;
- else
- g_object_set_data (G_OBJECT (surface), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
-
- /* Set XdndAware */
-
- /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
- XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
- GDK_SURFACE_XID (surface),
- gdk_x11_get_xatom_by_name_for_display (display, "XdndAware"),
- XA_ATOM, 32, PropModeReplace,
- (guchar *)&xdnd_version, 1);
-}
-
-static GdkSurface *
-gdk_x11_drag_get_drag_surface (GdkDrag *drag)
-{
- return GDK_X11_DRAG (drag)->drag_surface;
-}
-
-static void
-gdk_x11_drag_set_hotspot (GdkDrag *drag,
- gint hot_x,
- gint hot_y)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
-
- x11_drag->hot_x = hot_x;
- x11_drag->hot_y = hot_y;
-
- if (x11_drag->grab_seat)
- {
- /* DnD is managed, update current position */
- move_drag_surface (drag, x11_drag->last_x, x11_drag->last_y);
- }
-}
-
-static void
-gdk_x11_drag_default_output_done (GObject *drag,
- GAsyncResult *result,
- gpointer user_data)
-{
- GError *error = NULL;
-
- if (!gdk_drag_write_finish (GDK_DRAG (drag), result, &error))
- {
- GDK_DISPLAY_NOTE (gdk_drag_get_display (GDK_DRAG (drag)), DND, g_printerr ("failed to write stream: %s\n", error->message));
- g_error_free (error);
- }
-}
-
-static void
-gdk_x11_drag_default_output_handler (GOutputStream *stream,
- const char *mime_type,
- gpointer user_data)
-{
- gdk_drag_write_async (GDK_DRAG (user_data),
- mime_type,
- stream,
- G_PRIORITY_DEFAULT,
- NULL,
- gdk_x11_drag_default_output_done,
- NULL);
- g_object_unref (stream);
-}
-
-static gboolean
-gdk_x11_drag_xevent (GdkDisplay *display,
- const XEvent *xevent,
- gpointer data)
-{
- GdkDrag *drag = GDK_DRAG (data);
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
- Window xwindow;
- Atom xselection;
-
- xwindow = GDK_SURFACE_XID (x11_drag->ipc_surface);
- xselection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
-
- if (xevent->xany.window != xwindow)
- return FALSE;
-
- switch (xevent->type)
- {
- case SelectionClear:
- if (xevent->xselectionclear.selection != xselection)
- return FALSE;
-
- if (xevent->xselectionclear.time < x11_drag->timestamp)
- {
- GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("ignoring SelectionClear with too old timestamp (%lu vs %lu)\n",
- xevent->xselectionclear.time, x11_drag->timestamp));
- return FALSE;
- }
-
- GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("got SelectionClear, aborting DND\n"));
- gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR);
- return TRUE;
-
- case SelectionRequest:
- {
- const char *target, *property;
-
- if (xevent->xselectionrequest.selection != xselection)
- return FALSE;
-
- target = gdk_x11_get_xatom_name_for_display (display, xevent->xselectionrequest.target);
- if (xevent->xselectionrequest.property == None)
- property = target;
- else
- property = gdk_x11_get_xatom_name_for_display (display, xevent->xselectionrequest.property);
-
- if (xevent->xselectionrequest.requestor == None)
- {
- GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("got SelectionRequest for %s @ %s with NULL window, ignoring\n",
- target, property));
- return TRUE;
- }
-
- GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("got SelectionRequest for %s @ %s\n",
- target, property));
-
- gdk_x11_selection_output_streams_create (display,
- gdk_drag_get_formats (drag),
- xevent->xselectionrequest.requestor,
- xevent->xselectionrequest.selection,
- xevent->xselectionrequest.target,
- xevent->xselectionrequest.property ? xevent->xselectionrequest.property
- : xevent->xselectionrequest.target,
- xevent->xselectionrequest.time,
- gdk_x11_drag_default_output_handler,
- drag);
- return TRUE;
- }
-
- case ClientMessage:
- if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus"))
- gdk_x11_drag_handle_status (display, xevent);
- else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished"))
- gdk_x11_drag_handle_finished (display, xevent);
- else
- return FALSE;
- return TRUE;
-
- default:
- return FALSE;
- }
-}
-
-static double
-ease_out_cubic (double t)
-{
- double p = t - 1;
- return p * p * p + 1;
-}
-
-
-#define ANIM_TIME 500000 /* half a second */
-
-typedef struct _GdkDragAnim GdkDragAnim;
-struct _GdkDragAnim {
- GdkX11Drag *drag;
- GdkFrameClock *frame_clock;
- gint64 start_time;
-};
-
-static void
-gdk_drag_anim_destroy (GdkDragAnim *anim)
-{
- g_object_unref (anim->drag);
- g_slice_free (GdkDragAnim, anim);
-}
-
-static gboolean
-gdk_drag_anim_timeout (gpointer data)
-{
- GdkDragAnim *anim = data;
- GdkX11Drag *drag = anim->drag;
- GdkFrameClock *frame_clock = anim->frame_clock;
- gint64 current_time;
- double f;
- double t;
-
- if (!frame_clock)
- return G_SOURCE_REMOVE;
-
- current_time = gdk_frame_clock_get_frame_time (frame_clock);
-
- f = (current_time - anim->start_time) / (double) ANIM_TIME;
-
- if (f >= 1.0)
- return G_SOURCE_REMOVE;
-
- t = ease_out_cubic (f);
-
- gdk_surface_show (drag->drag_surface);
- gdk_surface_move (drag->drag_surface,
- drag->last_x + (drag->start_x - drag->last_x) * t,
- drag->last_y + (drag->start_y - drag->last_y) * t);
- gdk_surface_set_opacity (drag->drag_surface, 1.0 - f);
-
- return G_SOURCE_CONTINUE;
-}
-
-static void
-gdk_x11_drag_release_selection (GdkDrag *drag)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
- GdkDisplay *display;
- Display *xdisplay;
- Window xwindow;
- Atom xselection;
-
- display = gdk_drag_get_display (drag);
- xdisplay = GDK_DISPLAY_XDISPLAY (display);
- xselection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
- xwindow = GDK_SURFACE_XID (x11_drag->ipc_surface);
-
- if (XGetSelectionOwner (xdisplay, xselection) == xwindow)
- XSetSelectionOwner (xdisplay, xselection, None, CurrentTime);
-}
-
-static void
-gdk_x11_drag_drop_done (GdkDrag *drag,
- gboolean success)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
- GdkDragAnim *anim;
-/*
- cairo_surface_t *win_surface;
- cairo_surface_t *surface;
- cairo_t *cr;
-*/
- guint id;
-
- gdk_x11_drag_release_selection (drag);
-
- g_signal_handlers_disconnect_by_func (gdk_drag_get_display (drag),
- gdk_x11_drag_xevent,
- drag);
- if (success)
- {
- gdk_surface_hide (x11_drag->drag_surface);
- return;
- }
-
-/*
- win_surface = _gdk_surface_ref_cairo_surface (x11_drag->drag_surface);
- surface = gdk_surface_create_similar_surface (x11_drag->drag_surface,
- cairo_surface_get_content (win_surface),
- gdk_surface_get_width (x11_drag->drag_surface),
- gdk_surface_get_height (x11_drag->drag_surface));
- cr = cairo_create (surface);
- cairo_set_source_surface (cr, win_surface, 0, 0);
- cairo_paint (cr);
- cairo_destroy (cr);
- cairo_surface_destroy (win_surface);
-
- pattern = cairo_pattern_create_for_surface (surface);
-
- gdk_surface_set_background_pattern (x11_drag->drag_surface, pattern);
-
- cairo_pattern_destroy (pattern);
- cairo_surface_destroy (surface);
-*/
-
- anim = g_slice_new0 (GdkDragAnim);
- anim->drag = g_object_ref (x11_drag);
- anim->frame_clock = gdk_surface_get_frame_clock (x11_drag->drag_surface);
- anim->start_time = gdk_frame_clock_get_frame_time (anim->frame_clock);
-
- id = g_timeout_add_full (G_PRIORITY_DEFAULT, 17,
- gdk_drag_anim_timeout, anim,
- (GDestroyNotify) gdk_drag_anim_destroy);
- g_source_set_name_by_id (id, "[gtk+] gdk_drag_anim_timeout");
-}
-
-static gboolean
-drag_grab (GdkDrag *drag)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
- GdkDevice *device = gdk_drag_get_device (drag);
- GdkSeatCapabilities capabilities;
- GdkDisplay *display;
- Window root;
- GdkSeat *seat;
- gint keycode, i;
- GdkCursor *cursor;
-
- if (!x11_drag->ipc_surface)
- return FALSE;
-
- display = gdk_drag_get_display (drag);
- root = GDK_DISPLAY_XROOTWIN (display);
- seat = gdk_device_get_seat (gdk_drag_get_device (drag));
-
-#ifdef XINPUT_2
- if (GDK_IS_X11_DEVICE_XI2 (device))
- capabilities = GDK_SEAT_CAPABILITY_ALL_POINTING;
- else
-#endif
- capabilities = GDK_SEAT_CAPABILITY_ALL;
-
- cursor = gdk_drag_get_cursor (drag, x11_drag->current_action);
- g_set_object (&x11_drag->cursor, cursor);
-
- if (gdk_seat_grab (seat, x11_drag->ipc_surface,
- capabilities, FALSE,
- x11_drag->cursor, NULL, NULL, NULL) != GDK_GRAB_SUCCESS)
- return FALSE;
-
- g_set_object (&x11_drag->grab_seat, seat);
-
- gdk_x11_display_error_trap_push (display);
-
- for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
- {
- keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
- grab_keys[i].keysym);
- if (keycode == NoSymbol)
- continue;
-
-#ifdef XINPUT_2
- if (GDK_IS_X11_DEVICE_XI2 (device))
- {
- gint deviceid = gdk_x11_device_get_id (gdk_seat_get_keyboard (seat));
- unsigned char mask[XIMaskLen(XI_LASTEVENT)];
- XIGrabModifiers mods;
- XIEventMask evmask;
- gint num_mods;
-
- memset (mask, 0, sizeof (mask));
- XISetMask (mask, XI_KeyPress);
- XISetMask (mask, XI_KeyRelease);
-
- evmask.deviceid = deviceid;
- evmask.mask_len = sizeof (mask);
- evmask.mask = mask;
-
- num_mods = 1;
- mods.modifiers = grab_keys[i].modifiers;
-
- XIGrabKeycode (GDK_DISPLAY_XDISPLAY (display),
- deviceid,
- keycode,
- root,
- GrabModeAsync,
- GrabModeAsync,
- False,
- &evmask,
- num_mods,
- &mods);
- }
- else
-#endif
- {
- XGrabKey (GDK_DISPLAY_XDISPLAY (display),
- keycode, grab_keys[i].modifiers,
- root,
- FALSE,
- GrabModeAsync,
- GrabModeAsync);
- }
- }
-
- gdk_x11_display_error_trap_pop_ignored (display);
-
- return TRUE;
-}
-
-static void
-drag_ungrab (GdkDrag *drag)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
- GdkDisplay *display;
- GdkDevice *keyboard;
- Window root;
- gint keycode, i;
-
- if (!x11_drag->grab_seat)
- return;
-
- gdk_seat_ungrab (x11_drag->grab_seat);
-
- display = gdk_drag_get_display (drag);
- keyboard = gdk_seat_get_keyboard (x11_drag->grab_seat);
- root = GDK_DISPLAY_XROOTWIN (display);
- g_clear_object (&x11_drag->grab_seat);
-
- for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
- {
- keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
- grab_keys[i].keysym);
- if (keycode == NoSymbol)
- continue;
-
-#ifdef XINPUT_2
- if (GDK_IS_X11_DEVICE_XI2 (keyboard))
- {
- XIGrabModifiers mods;
- gint num_mods;
-
- num_mods = 1;
- mods.modifiers = grab_keys[i].modifiers;
-
- XIUngrabKeycode (GDK_DISPLAY_XDISPLAY (display),
- gdk_x11_device_get_id (keyboard),
- keycode,
- root,
- num_mods,
- &mods);
- }
- else
-#endif /* XINPUT_2 */
- {
- XUngrabKey (GDK_DISPLAY_XDISPLAY (display),
- keycode, grab_keys[i].modifiers,
- root);
- }
- }
-}
-
-GdkDrag *
-_gdk_x11_surface_drag_begin (GdkSurface *surface,
- GdkDevice *device,
- GdkContentProvider *content,
- GdkDragAction actions,
- gint dx,
- gint dy)
-{
- GdkX11Drag *x11_drag;
- GdkDrag *drag;
- GdkDisplay *display;
- int x_root, y_root;
- Atom xselection;
- GdkSurface *ipc_surface;
-
- display = gdk_surface_get_display (surface);
-
- ipc_surface = gdk_surface_new_popup (display, &(GdkRectangle) { -99, -99, 1, 1 });
-
- drag = (GdkDrag *) g_object_new (GDK_TYPE_X11_DRAG,
- "device", device,
- "content", content,
- "surface", ipc_surface,
- NULL);
- x11_drag = GDK_X11_DRAG (drag);
-
- g_signal_connect (display, "xevent", G_CALLBACK (gdk_x11_drag_xevent), drag);
-
- precache_target_list (drag);
-
- gdk_device_get_position (device, &x_root, &y_root);
- x_root += dx;
- y_root += dy;
-
- x11_drag->start_x = x_root;
- x11_drag->start_y = y_root;
- x11_drag->last_x = x_root;
- x11_drag->last_y = y_root;
-
- x11_drag->protocol = GDK_DRAG_PROTO_XDND;
- x11_drag->actions = actions;
- x11_drag->ipc_surface = ipc_surface;
- if (gdk_surface_get_group (surface))
- gdk_surface_set_group (x11_drag->ipc_surface, surface);
- gdk_surface_show (x11_drag->ipc_surface);
-
- x11_drag->drag_surface = create_drag_surface (display);
-
- if (!drag_grab (drag))
- {
- g_object_unref (drag);
- return NULL;
- }
-
- move_drag_surface (drag, x_root, y_root);
-
- x11_drag->timestamp = gdk_display_get_last_seen_time (display);
- xselection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
- XSetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
- xselection,
- GDK_SURFACE_XID (x11_drag->ipc_surface),
- x11_drag->timestamp);
- if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), xselection) != GDK_SURFACE_XID (x11_drag->ipc_surface))
- {
- GDK_DISPLAY_NOTE (display, DND, g_printerr ("failed XSetSelectionOwner() on \"XdndSelection\", aborting DND\n"));
- g_object_unref (drag);
- return NULL;
- }
-
- return drag;
-}
-
-static void
-gdk_x11_drag_set_cursor (GdkDrag *drag,
- GdkCursor *cursor)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
-
- if (!g_set_object (&x11_drag->cursor, cursor))
- return;
-
- if (x11_drag->grab_seat)
- {
- G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
- gdk_device_grab (gdk_seat_get_pointer (x11_drag->grab_seat),
- x11_drag->ipc_surface,
- GDK_OWNERSHIP_APPLICATION, FALSE,
- GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
- cursor, GDK_CURRENT_TIME);
- G_GNUC_END_IGNORE_DEPRECATIONS;
- }
-}
-
-static void
-gdk_x11_drag_cancel (GdkDrag *drag,
- GdkDragCancelReason reason)
-{
- drag_ungrab (drag);
- gdk_drag_drop_done (drag, FALSE);
-}
-
-static void
-gdk_x11_drag_drop_performed (GdkDrag *drag,
- guint32 time_)
-{
- gdk_x11_drag_drop (drag, time_);
- drag_ungrab (drag);
-}
-
-#define BIG_STEP 20
-#define SMALL_STEP 1
-
-static void
-gdk_drag_get_current_actions (GdkModifierType state,
- gint button,
- GdkDragAction actions,
- GdkDragAction *suggested_action,
- GdkDragAction *possible_actions)
-{
- *suggested_action = 0;
- *possible_actions = 0;
-
- if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
- {
- *suggested_action = GDK_ACTION_ASK;
- *possible_actions = actions;
- }
- else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
- {
- if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
- {
- if (actions & GDK_ACTION_LINK)
- {
- *suggested_action = GDK_ACTION_LINK;
- *possible_actions = GDK_ACTION_LINK;
- }
- }
- else if (state & GDK_CONTROL_MASK)
- {
- if (actions & GDK_ACTION_COPY)
- {
- *suggested_action = GDK_ACTION_COPY;
- *possible_actions = GDK_ACTION_COPY;
- }
- }
- else
- {
- if (actions & GDK_ACTION_MOVE)
- {
- *suggested_action = GDK_ACTION_MOVE;
- *possible_actions = GDK_ACTION_MOVE;
- }
- }
- }
- else
- {
- *possible_actions = actions;
-
- if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
- *suggested_action = GDK_ACTION_ASK;
- else if (actions & GDK_ACTION_COPY)
- *suggested_action = GDK_ACTION_COPY;
- else if (actions & GDK_ACTION_MOVE)
- *suggested_action = GDK_ACTION_MOVE;
- else if (actions & GDK_ACTION_LINK)
- *suggested_action = GDK_ACTION_LINK;
- }
-}
-
-static void
-gdk_drag_update (GdkDrag *drag,
- gdouble x_root,
- gdouble y_root,
- GdkModifierType mods,
- guint32 evtime)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
- GdkDragAction suggested_action;
- GdkDragAction possible_actions;
- GdkDragProtocol protocol;
- Window proxy;
-
- gdk_drag_get_current_actions (mods, GDK_BUTTON_PRIMARY, x11_drag->actions,
- &suggested_action, &possible_actions);
-
- proxy = gdk_x11_drag_find_surface (drag,
- x11_drag->drag_surface,
- x_root, y_root, &protocol);
-
- gdk_x11_drag_drag_motion (drag, proxy, protocol, x_root, y_root,
- suggested_action, possible_actions, evtime);
-}
-
-static gboolean
-gdk_dnd_handle_motion_event (GdkDrag *drag,
- const GdkEventMotion *event)
-{
- GdkModifierType state;
-
- if (!gdk_event_get_state ((GdkEvent *) event, &state))
- return FALSE;
-
- gdk_drag_update (drag, event->x_root, event->y_root, state,
- gdk_event_get_time ((GdkEvent *) event));
- return TRUE;
-}
-
-static gboolean
-gdk_dnd_handle_key_event (GdkDrag *drag,
- const GdkEventKey *event)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
- GdkModifierType state;
- GdkDevice *pointer;
- gint dx, dy;
-
- dx = dy = 0;
- state = event->state;
- pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
-
- if (event->any.type == GDK_KEY_PRESS)
- {
- switch (event->keyval)
- {
- case GDK_KEY_Escape:
- gdk_drag_cancel (drag, GDK_DRAG_CANCEL_USER_CANCELLED);
- return TRUE;
-
- case GDK_KEY_space:
- case GDK_KEY_Return:
- case GDK_KEY_ISO_Enter:
- case GDK_KEY_KP_Enter:
- case GDK_KEY_KP_Space:
- if ((gdk_drag_get_selected_action (drag) != 0) &&
- (x11_drag->proxy_xid != None))
- {
- g_signal_emit_by_name (drag, "drop-performed");
- }
- else
- gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
-
- return TRUE;
-
- case GDK_KEY_Up:
- case GDK_KEY_KP_Up:
- dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
- break;
-
- case GDK_KEY_Down:
- case GDK_KEY_KP_Down:
- dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
- break;
-
- case GDK_KEY_Left:
- case GDK_KEY_KP_Left:
- dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
- break;
-
- case GDK_KEY_Right:
- case GDK_KEY_KP_Right:
- dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
- break;
-
- default:
- break;
- }
- }
-
- /* The state is not yet updated in the event, so we need
- * to query it here. We could use XGetModifierMapping, but
- * that would be overkill.
- */
- _gdk_device_query_state (pointer, NULL, NULL, NULL, NULL, NULL, NULL, &state);
-
- if (dx != 0 || dy != 0)
- {
- x11_drag->last_x += dx;
- x11_drag->last_y += dy;
- gdk_device_warp (pointer, x11_drag->last_x, x11_drag->last_y);
- }
-
- gdk_drag_update (drag, x11_drag->last_x, x11_drag->last_y, state,
- gdk_event_get_time ((GdkEvent *) event));
-
- return TRUE;
-}
-
-static gboolean
-gdk_dnd_handle_grab_broken_event (GdkDrag *drag,
- const GdkEventGrabBroken *event)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
-
- /* Don't cancel if we break the implicit grab from the initial button_press.
- * Also, don't cancel if we re-grab on the widget or on our IPC window, for
- * example, when changing the drag cursor.
- */
- if (event->implicit ||
- event->grab_surface == x11_drag->drag_surface ||
- event->grab_surface == x11_drag->ipc_surface)
- return FALSE;
-
- if (gdk_event_get_device ((GdkEvent *) event) !=
- gdk_drag_get_device (drag))
- return FALSE;
-
- gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR);
- return TRUE;
-}
-
-static gboolean
-gdk_dnd_handle_button_event (GdkDrag *drag,
- const GdkEventButton *event)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
-
-#if 0
- /* FIXME: Check the button matches */
- if (event->button != x11_drag->button)
- return FALSE;
-#endif
-
- if ((gdk_drag_get_selected_action (drag) != 0) &&
- (x11_drag->proxy_xid != None))
- {
- g_signal_emit_by_name (drag, "drop-performed");
- }
- else
- gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
-
- return TRUE;
-}
-
-gboolean
-gdk_x11_drag_handle_event (GdkDrag *drag,
- const GdkEvent *event)
-{
- GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
-
- if (!x11_drag->grab_seat)
- return FALSE;
-
- switch ((guint) event->any.type)
- {
- case GDK_MOTION_NOTIFY:
- return gdk_dnd_handle_motion_event (drag, &event->motion);
- case GDK_BUTTON_RELEASE:
- return gdk_dnd_handle_button_event (drag, &event->button);
- case GDK_KEY_PRESS:
- case GDK_KEY_RELEASE:
- return gdk_dnd_handle_key_event (drag, &event->key);
- case GDK_GRAB_BROKEN:
- return gdk_dnd_handle_grab_broken_event (drag, &event->grab_broken);
- default:
- break;
- }
-
- return FALSE;
-}
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkx11dnd.h"
+
+#include "gdk-private.h"
+#include "gdkasync.h"
+#include "gdkclipboardprivate.h"
+#include "gdkclipboard-x11.h"
+#include "gdkdeviceprivate.h"
+#include "gdkdisplay-x11.h"
+#include "gdkdragprivate.h"
+#include "gdkinternals.h"
+#include "gdkintl.h"
+#include "gdkproperty.h"
+#include "gdkprivate-x11.h"
+#include "gdkscreen-x11.h"
+#include "gdkselectioninputstream-x11.h"
+#include "gdkselectionoutputstream-x11.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#ifdef HAVE_XCOMPOSITE
+#include <X11/extensions/Xcomposite.h>
+#endif
+
+#include <string.h>
+
+typedef enum {
+ GDK_DRAG_STATUS_DRAG,
+ GDK_DRAG_STATUS_MOTION_WAIT,
+ GDK_DRAG_STATUS_ACTION_WAIT,
+ GDK_DRAG_STATUS_DROP
+} GtkDragStatus;
+
+/*
+ * GdkDragProtocol:
+ * @GDK_DRAG_PROTO_NONE: no protocol.
+ * @GDK_DRAG_PROTO_XDND: The Xdnd protocol.
+ * @GDK_DRAG_PROTO_ROOTWIN: An extension to the Xdnd protocol for
+ * unclaimed root window drops.
+ *
+ * Used in #GdkDrag to indicate the protocol according to
+ * which DND is done.
+ */
+typedef enum
+{
+ GDK_DRAG_PROTO_NONE = 0,
+ GDK_DRAG_PROTO_XDND,
+ GDK_DRAG_PROTO_ROOTWIN
+} GdkDragProtocol;
+
+typedef struct {
+ guint32 xid;
+ gint x, y, width, height;
+ gboolean mapped;
+ gboolean shape_selected;
+ gboolean shape_valid;
+ cairo_region_t *shape;
+} GdkCacheChild;
+
+struct _GdkSurfaceCache {
+ GList *children;
+ GHashTable *child_hash;
+ guint old_event_mask;
+ GdkDisplay *display;
+ gint ref_count;
+};
+
+
+struct _GdkX11Drag
+{
+ GdkDrag drag;
+
+ GdkDragProtocol protocol;
+
+ gint start_x; /* Where the drag started */
+ gint start_y;
+ guint16 last_x; /* Coordinates from last event */
+ guint16 last_y;
+ gulong timestamp; /* Timestamp we claimed the DND selection with */
+ GdkDragAction xdnd_actions; /* What is currently set in XdndActionList */
+ guint version; /* Xdnd protocol version */
+
+ GdkSurfaceCache *cache;
+
+ GdkSurface *drag_surface;
+
+ GdkSurface *ipc_surface;
+ GdkCursor *cursor;
+ GdkSeat *grab_seat;
+ GdkDragAction actions;
+ GdkDragAction current_action;
+
+ gint hot_x;
+ gint hot_y;
+
+ Window dest_xid; /* The last window we looked up */
+ Window proxy_xid; /* The proxy window for dest_xid (or dest_xid if no proxying happens) */
+ Window drop_xid; /* The (non-proxied) window that is receiving drops */
+ guint xdnd_targets_set : 1; /* Whether we've already set XdndTypeList */
+ guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
+ guint drag_status : 4; /* current status of drag */
+ guint drop_failed : 1; /* Whether the drop was unsuccessful */
+};
+
+struct _GdkX11DragClass
+{
+ GdkDragClass parent_class;
+};
+
+typedef struct {
+ gint keysym;
+ gint modifiers;
+} GrabKey;
+
+static GrabKey grab_keys[] = {
+ { XK_Escape, 0 },
+ { XK_space, 0 },
+ { XK_KP_Space, 0 },
+ { XK_Return, 0 },
+ { XK_KP_Enter, 0 },
+ { XK_Up, 0 },
+ { XK_Up, Mod1Mask },
+ { XK_Down, 0 },
+ { XK_Down, Mod1Mask },
+ { XK_Left, 0 },
+ { XK_Left, Mod1Mask },
+ { XK_Right, 0 },
+ { XK_Right, Mod1Mask },
+ { XK_KP_Up, 0 },
+ { XK_KP_Up, Mod1Mask },
+ { XK_KP_Down, 0 },
+ { XK_KP_Down, Mod1Mask },
+ { XK_KP_Left, 0 },
+ { XK_KP_Left, Mod1Mask },
+ { XK_KP_Right, 0 },
+ { XK_KP_Right, Mod1Mask }
+};
+
+/* Forward declarations */
+
+static GdkSurfaceCache *gdk_surface_cache_ref (GdkSurfaceCache *cache);
+static void gdk_surface_cache_unref (GdkSurfaceCache *cache);
+
+gboolean gdk_x11_drag_handle_event (GdkDrag *drag,
+ const GdkEvent *event);
+
+static GList *drags;
+static GSList *window_caches;
+
+G_DEFINE_TYPE (GdkX11Drag, gdk_x11_drag, GDK_TYPE_DRAG)
+
+static void
+gdk_x11_drag_init (GdkX11Drag *drag)
+{
+ drags = g_list_prepend (drags, drag);
+}
+
+static void gdk_x11_drag_finalize (GObject *object);
+static Window gdk_x11_drag_find_surface (GdkDrag *drag,
+ GdkSurface *drag_surface,
+ gint x_root,
+ gint y_root,
+ GdkDragProtocol *protocol);
+static gboolean gdk_x11_drag_drag_motion (GdkDrag *drag,
+ Window proxy_xid,
+ GdkDragProtocol protocol,
+ gint x_root,
+ gint y_root,
+ GdkDragAction suggested_action,
+ GdkDragAction possible_actions,
+ guint32 time);
+static void gdk_x11_drag_drop (GdkDrag *drag,
+ guint32 time_);
+static GdkSurface * gdk_x11_drag_get_drag_surface (GdkDrag *drag);
+static void gdk_x11_drag_set_hotspot (GdkDrag *drag,
+ gint hot_x,
+ gint hot_y);
+static void gdk_x11_drag_drop_done (GdkDrag *drag,
+ gboolean success);
+static void gdk_x11_drag_set_cursor (GdkDrag *drag,
+ GdkCursor *cursor);
+static void gdk_x11_drag_cancel (GdkDrag *drag,
+ GdkDragCancelReason reason);
+static void gdk_x11_drag_drop_performed (GdkDrag *drag,
+ guint32 time);
+
+static void
+gdk_x11_drag_class_init (GdkX11DragClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdkDragClass *drag_class = GDK_DRAG_CLASS (klass);
+
+ object_class->finalize = gdk_x11_drag_finalize;
+
+ drag_class->get_drag_surface = gdk_x11_drag_get_drag_surface;
+ drag_class->set_hotspot = gdk_x11_drag_set_hotspot;
+ drag_class->drop_done = gdk_x11_drag_drop_done;
+ drag_class->set_cursor = gdk_x11_drag_set_cursor;
+ drag_class->cancel = gdk_x11_drag_cancel;
+ drag_class->drop_performed = gdk_x11_drag_drop_performed;
+ drag_class->handle_event = gdk_x11_drag_handle_event;
+}
+
+static void
+gdk_x11_drag_finalize (GObject *object)
+{
+ GdkDrag *drag = GDK_DRAG (object);
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (object);
+ GdkSurface *drag_surface, *ipc_surface;
+
+ if (x11_drag->cache)
+ gdk_surface_cache_unref (x11_drag->cache);
+
+ drags = g_list_remove (drags, drag);
+
+ drag_surface = x11_drag->drag_surface;
+ ipc_surface = x11_drag->ipc_surface;
+
+ G_OBJECT_CLASS (gdk_x11_drag_parent_class)->finalize (object);
+
+ if (drag_surface)
+ gdk_surface_destroy (drag_surface);
+ if (ipc_surface)
+ gdk_surface_destroy (ipc_surface);
+}
+
+/* Drag Contexts */
+
+GdkDrag *
+gdk_x11_drag_find (GdkDisplay *display,
+ Window source_xid,
+ Window dest_xid)
+{
+ GList *tmp_list;
+ GdkDrag *drag;
+ GdkX11Drag *drag_x11;
+ Window drag_dest_xid;
+ GdkSurface *surface;
+ Window surface_xid;
+
+ for (tmp_list = drags; tmp_list; tmp_list = tmp_list->next)
+ {
+ drag = (GdkDrag *)tmp_list->data;
+ drag_x11 = (GdkX11Drag *)drag;
+
+ if (gdk_drag_get_display (drag) != display)
+ continue;
+
+ g_object_get (drag, "surface", &surface, NULL);
+ surface_xid = surface ? GDK_SURFACE_XID (surface) : None;
+ g_object_unref (surface);
+
+ drag_dest_xid = drag_x11->proxy_xid
+ ? (drag_x11->drop_xid
+ ? drag_x11->drop_xid
+ : drag_x11->proxy_xid)
+ : None;
+
+ if (((source_xid == None) || (surface && (surface_xid == source_xid))) &&
+ ((dest_xid == None) || (drag_dest_xid == dest_xid)))
+ return drag;
+ }
+
+ return NULL;
+}
+
+static void
+precache_target_list (GdkDrag *drag)
+{
+ GdkContentFormats *formats = gdk_drag_get_formats (drag);
+ const char * const *atoms;
+ gsize n_atoms;
+
+ atoms = gdk_content_formats_get_mime_types (formats, &n_atoms);
+
+ _gdk_x11_precache_atoms (gdk_drag_get_display (drag),
+ (const gchar **) atoms,
+ n_atoms);
+}
+
+/* Utility functions */
+
+static void
+free_cache_child (GdkCacheChild *child,
+ GdkDisplay *display)
+{
+ if (child->shape)
+ cairo_region_destroy (child->shape);
+
+ if (child->shape_selected && display)
+ {
+ GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+
+ XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
+ }
+
+ g_free (child);
+}
+
+static void
+gdk_surface_cache_add (GdkSurfaceCache *cache,
+ guint32 xid,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ gboolean mapped)
+{
+ GdkCacheChild *child = g_new (GdkCacheChild, 1);
+
+ child->xid = xid;
+ child->x = x;
+ child->y = y;
+ child->width = width;
+ child->height = height;
+ child->mapped = mapped;
+ child->shape_selected = FALSE;
+ child->shape_valid = FALSE;
+ child->shape = NULL;
+
+ cache->children = g_list_prepend (cache->children, child);
+ g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
+ cache->children);
+}
+
+GdkFilterReturn
+gdk_surface_cache_shape_filter (const XEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ GdkSurfaceCache *cache = data;
+
+ GdkX11Display *display = GDK_X11_DISPLAY (cache->display);
+
+ if (display->have_shapes &&
+ xevent->type == display->shape_event_base + ShapeNotify)
+ {
+ XShapeEvent *xse = (XShapeEvent*)xevent;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xse->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+ child->shape_valid = FALSE;
+ if (child->shape)
+ {
+ cairo_region_destroy (child->shape);
+ child->shape = NULL;
+ }
+ }
+
+ return GDK_FILTER_REMOVE;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+GdkFilterReturn
+gdk_surface_cache_filter (const XEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ GdkSurfaceCache *cache = data;
+
+ switch (xevent->type)
+ {
+ case CirculateNotify:
+ break;
+ case ConfigureNotify:
+ {
+ const XConfigureEvent *xce = &xevent->xconfigure;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xce->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+ child->x = xce->x;
+ child->y = xce->y;
+ child->width = xce->width;
+ child->height = xce->height;
+ if (xce->above == None && (node->next))
+ {
+ GList *last = g_list_last (cache->children);
+ cache->children = g_list_remove_link (cache->children, node);
+ last->next = node;
+ node->next = NULL;
+ node->prev = last;
+ }
+ else
+ {
+ GList *above_node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xce->above));
+ if (above_node && node->next != above_node)
+ {
+ /* Put the window above (before in the list) above_node */
+ cache->children = g_list_remove_link (cache->children, node);
+ node->prev = above_node->prev;
+ if (node->prev)
+ node->prev->next = node;
+ else
+ cache->children = node;
+ node->next = above_node;
+ above_node->prev = node;
+ }
+ }
+ }
+ break;
+ }
+ case CreateNotify:
+ {
+ const XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
+
+ if (!g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xcwe->window)))
+ gdk_surface_cache_add (cache, xcwe->window,
+ xcwe->x, xcwe->y, xcwe->width, xcwe->height,
+ FALSE);
+ break;
+ }
+ case DestroyNotify:
+ {
+ const XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xdwe->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+
+ g_hash_table_remove (cache->child_hash,
+ GUINT_TO_POINTER (xdwe->window));
+ cache->children = g_list_remove_link (cache->children, node);
+ /* window is destroyed, no need to disable ShapeNotify */
+ free_cache_child (child, NULL);
+ g_list_free_1 (node);
+ }
+ break;
+ }
+ case MapNotify:
+ {
+ const XMapEvent *xme = &xevent->xmap;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xme->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+ child->mapped = TRUE;
+ }
+ break;
+ }
+ case ReparentNotify:
+ break;
+ case UnmapNotify:
+ {
+ const XMapEvent *xume = &xevent->xmap;
+ GList *node;
+
+ node = g_hash_table_lookup (cache->child_hash,
+ GUINT_TO_POINTER (xume->window));
+ if (node)
+ {
+ GdkCacheChild *child = node->data;
+ child->mapped = FALSE;
+ }
+ break;
+ }
+ default:
+ return GDK_FILTER_CONTINUE;
+ }
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkSurfaceCache *
+gdk_surface_cache_new (GdkDisplay *display)
+{
+ XWindowAttributes xwa;
+ GdkX11Screen *screen = GDK_X11_DISPLAY (display)->screen;
+ Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
+ Window xroot_window = GDK_DISPLAY_XROOTWIN (display);
+ GdkChildInfoX11 *children;
+ guint nchildren, i;
+#ifdef HAVE_XCOMPOSITE
+ Window cow;
+#endif
+
+ GdkSurfaceCache *result = g_new (GdkSurfaceCache, 1);
+
+ result->children = NULL;
+ result->child_hash = g_hash_table_new (g_direct_hash, NULL);
+ result->display = display;
+ result->ref_count = 1;
+
+ XGetWindowAttributes (xdisplay, xroot_window, &xwa);
+ result->old_event_mask = xwa.your_event_mask;
+
+ if (G_UNLIKELY (!GDK_X11_DISPLAY (display)->trusted_client))
+ {
+ GList *toplevel_windows, *list;
+ GdkSurface *surface;
+ GdkSurfaceImplX11 *impl;
+ gint x, y, width, height;
+
+ toplevel_windows = gdk_x11_display_get_toplevel_windows (display);
+ for (list = toplevel_windows; list; list = list->next)
+ {
+ surface = GDK_SURFACE (list->data);
+ impl = GDK_SURFACE_IMPL_X11 (surface->impl);
+ gdk_surface_get_geometry (surface, &x, &y, &width, &height);
+ gdk_surface_cache_add (result, GDK_SURFACE_XID (surface),
+ x * impl->surface_scale, y * impl->surface_scale,
+ width * impl->surface_scale,
+ height * impl->surface_scale,
+ gdk_surface_is_visible (surface));
+ }
+ return result;
+ }
+
+ XSelectInput (xdisplay, xroot_window, result->old_event_mask | SubstructureNotifyMask);
+
+ if (!_gdk_x11_get_window_child_info (display,
+ xroot_window,
+ FALSE, NULL,
+ &children, &nchildren))
+ return result;
+
+ for (i = 0; i < nchildren ; i++)
+ {
+ gdk_surface_cache_add (result, children[i].window,
+ children[i].x, children[i].y, children[i].width, children[i].height,
+ children[i].is_mapped);
+ }
+
+ g_free (children);
+
+#ifdef HAVE_XCOMPOSITE
+ /*
+ * Add the composite overlay window to the cache, as this can be a reasonable
+ * Xdnd proxy as well.
+ * This is only done when the screen is composited in order to avoid mapping
+ * the COW. We assume that the CM is using the COW (which is true for pretty
+ * much any CM currently in use).
+ */
+ if (gdk_display_is_composited (display))
+ {
+ cow = XCompositeGetOverlayWindow (xdisplay, xroot_window);
+ gdk_surface_cache_add (result, cow, 0, 0,
+ WidthOfScreen (GDK_X11_SCREEN (screen)->xscreen),
+ HeightOfScreen (GDK_X11_SCREEN (screen)->xscreen),
+ TRUE);
+ XCompositeReleaseOverlayWindow (xdisplay, xroot_window);
+ }
+#endif
+
+ return result;
+}
+
+static void
+gdk_surface_cache_destroy (GdkSurfaceCache *cache)
+{
+ XSelectInput (GDK_DISPLAY_XDISPLAY (cache->display),
+ GDK_DISPLAY_XROOTWIN (cache->display),
+ cache->old_event_mask);
+
+ gdk_x11_display_error_trap_push (cache->display);
+ g_list_foreach (cache->children, (GFunc)free_cache_child, cache->display);
+ gdk_x11_display_error_trap_pop_ignored (cache->display);
+
+ g_list_free (cache->children);
+ g_hash_table_destroy (cache->child_hash);
+
+ g_free (cache);
+}
+
+static GdkSurfaceCache *
+gdk_surface_cache_ref (GdkSurfaceCache *cache)
+{
+ cache->ref_count += 1;
+
+ return cache;
+}
+
+static void
+gdk_surface_cache_unref (GdkSurfaceCache *cache)
+{
+ g_assert (cache->ref_count > 0);
+
+ cache->ref_count -= 1;
+
+ if (cache->ref_count == 0)
+ {
+ window_caches = g_slist_remove (window_caches, cache);
+ gdk_surface_cache_destroy (cache);
+ }
+}
+
+GdkSurfaceCache *
+gdk_surface_cache_get (GdkDisplay *display)
+{
+ GSList *list;
+ GdkSurfaceCache *cache;
+
+ for (list = window_caches; list; list = list->next)
+ {
+ cache = list->data;
+ if (cache->display == display)
+ return gdk_surface_cache_ref (cache);
+ }
+
+ cache = gdk_surface_cache_new (display);
+
+ window_caches = g_slist_prepend (window_caches, cache);
+
+ return cache;
+}
+
+static gboolean
+is_pointer_within_shape (GdkDisplay *display,
+ GdkCacheChild *child,
+ gint x_pos,
+ gint y_pos)
+{
+ if (!child->shape_selected)
+ {
+ GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+
+ XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
+ child->shape_selected = TRUE;
+ }
+ if (!child->shape_valid)
+ {
+ GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+ cairo_region_t *input_shape;
+
+ child->shape = NULL;
+ if (gdk_display_supports_shapes (display))
+ child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
+ child->xid, 1, ShapeBounding);
+#ifdef ShapeInput
+ input_shape = NULL;
+ if (gdk_display_supports_input_shapes (display))
+ input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
+ child->xid, 1, ShapeInput);
+
+ if (child->shape && input_shape)
+ {
+ cairo_region_intersect (child->shape, input_shape);
+ cairo_region_destroy (input_shape);
+ }
+ else if (input_shape)
+ {
+ child->shape = input_shape;
+ }
+#endif
+
+ child->shape_valid = TRUE;
+ }
+
+ return child->shape == NULL ||
+ cairo_region_contains_point (child->shape, x_pos, y_pos);
+}
+
+static Window
+get_client_window_at_coords_recurse (GdkDisplay *display,
+ Window win,
+ gboolean is_toplevel,
+ gint x,
+ gint y)
+{
+ GdkChildInfoX11 *children;
+ unsigned int nchildren;
+ int i;
+ gboolean found_child = FALSE;
+ GdkChildInfoX11 child = { 0, };
+ gboolean has_wm_state = FALSE;
+
+ if (!_gdk_x11_get_window_child_info (display, win, TRUE,
+ is_toplevel? &has_wm_state : NULL,
+ &children, &nchildren))
+ return None;
+
+ if (has_wm_state)
+ {
+ g_free (children);
+
+ return win;
+ }
+
+ for (i = nchildren - 1; (i >= 0) && !found_child; i--)
+ {
+ GdkChildInfoX11 *cur_child = &children[i];
+
+ if ((cur_child->is_mapped) && (cur_child->window_class == InputOutput) &&
+ (x >= cur_child->x) && (x < cur_child->x + cur_child->width) &&
+ (y >= cur_child->y) && (y < cur_child->y + cur_child->height))
+ {
+ x -= cur_child->x;
+ y -= cur_child->y;
+ child = *cur_child;
+ found_child = TRUE;
+ }
+ }
+
+ g_free (children);
+
+ if (found_child)
+ {
+ if (child.has_wm_state)
+ return child.window;
+ else
+ return get_client_window_at_coords_recurse (display, child.window, FALSE, x, y);
+ }
+ else
+ return None;
+}
+
+static Window
+get_client_window_at_coords (GdkSurfaceCache *cache,
+ Window ignore,
+ gint x_root,
+ gint y_root)
+{
+ GList *tmp_list;
+ Window retval = None;
+ GdkDisplay *display;
+
+ display = cache->display;
+
+ gdk_x11_display_error_trap_push (display);
+
+ tmp_list = cache->children;
+
+ while (tmp_list && !retval)
+ {
+ GdkCacheChild *child = tmp_list->data;
+
+ if ((child->xid != ignore) && (child->mapped))
+ {
+ if ((x_root >= child->x) && (x_root < child->x + child->width) &&
+ (y_root >= child->y) && (y_root < child->y + child->height))
+ {
+ if (!is_pointer_within_shape (display, child,
+ x_root - child->x,
+ y_root - child->y))
+ {
+ tmp_list = tmp_list->next;
+ continue;
+ }
+
+ retval = get_client_window_at_coords_recurse (display,
+ child->xid, TRUE,
+ x_root - child->x,
+ y_root - child->y);
+ if (!retval)
+ retval = child->xid;
+ }
+ }
+ tmp_list = tmp_list->next;
+ }
+
+ gdk_x11_display_error_trap_pop_ignored (display);
+
+ if (retval)
+ return retval;
+ else
+ return GDK_DISPLAY_XROOTWIN (display);
+}
+
+/*************************************************************
+ ***************************** XDND **************************
+ *************************************************************/
+
+/* Utility functions */
+
+static struct {
+ const gchar *name;
+ GdkDragAction action;
+} xdnd_actions_table[] = {
+ { "XdndActionCopy", GDK_ACTION_COPY },
+ { "XdndActionMove", GDK_ACTION_MOVE },
+ { "XdndActionLink", GDK_ACTION_LINK },
+ { "XdndActionAsk", GDK_ACTION_ASK },
+ { "XdndActionPrivate", GDK_ACTION_COPY },
+ };
+
+static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
+
+static GdkDragAction
+xdnd_action_from_atom (GdkDisplay *display,
+ Atom xatom)
+{
+ const char *name;
+ gint i;
+
+ if (xatom == None)
+ return 0;
+
+ name = gdk_x11_get_xatom_name_for_display (display, xatom);
+
+ for (i = 0; i < xdnd_n_actions; i++)
+ if (g_str_equal (name, xdnd_actions_table[i].name))
+ return xdnd_actions_table[i].action;
+
+ return 0;
+}
+
+static Atom
+xdnd_action_to_atom (GdkDisplay *display,
+ GdkDragAction action)
+{
+ gint i;
+
+ for (i = 0; i < xdnd_n_actions; i++)
+ if (action == xdnd_actions_table[i].action)
+ return gdk_x11_get_xatom_by_name_for_display (display, xdnd_actions_table[i].name);
+
+ return None;
+}
+
+/* Source side */
+
+void
+gdk_x11_drag_handle_status (GdkDisplay *display,
+ const XEvent *xevent)
+{
+ guint32 dest_surface = xevent->xclient.data.l[0];
+ guint32 flags = xevent->xclient.data.l[1];
+ Atom action = xevent->xclient.data.l[4];
+ GdkDrag *drag;
+
+ drag = gdk_x11_drag_find (display, xevent->xclient.window, dest_surface);
+
+ GDK_DISPLAY_NOTE (display, DND,
+ g_message ("XdndStatus: dest_surface: %#x action: %ld",
+ dest_surface, action));
+
+ if (drag)
+ {
+ GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
+ if (drag_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
+ drag_x11->drag_status = GDK_DRAG_STATUS_DRAG;
+
+ if (!(action != 0) != !(flags & 1))
+ {
+ GDK_DISPLAY_NOTE (display, DND,
+ g_warning ("Received status event with flags not corresponding to action!"));
+ action = 0;
+ }
+
+ gdk_drag_set_selected_action (drag, xdnd_action_from_atom (display, action));
+ drag_x11->current_action = action;
+ }
+}
+
+void
+gdk_x11_drag_handle_finished (GdkDisplay *display,
+ const XEvent *xevent)
+{
+ guint32 dest_surface = xevent->xclient.data.l[0];
+ GdkDrag *drag;
+ GdkX11Drag *drag_x11;
+
+ drag = gdk_x11_drag_find (display, xevent->xclient.window, dest_surface);
+
+ GDK_DISPLAY_NOTE (display, DND,
+ g_message ("XdndFinished: dest_surface: %#x", dest_surface));
+
+ if (drag)
+ {
+ g_object_ref (drag);
+
+ drag_x11 = GDK_X11_DRAG (drag);
+ if (drag_x11->version == 5)
+ drag_x11->drop_failed = xevent->xclient.data.l[1] == 0;
+
+ g_signal_emit_by_name (drag, "dnd-finished");
+ gdk_drag_drop_done (drag, !drag_x11->drop_failed);
+
+ g_object_unref (drag);
+ }
+}
+
+static void
+xdnd_set_targets (GdkX11Drag *drag_x11)
+{
+ GdkDrag *drag = GDK_DRAG (drag_x11);
+ Atom *atomlist;
+ const char * const *atoms;
+ gsize i, n_atoms;
+ GdkDisplay *display = gdk_drag_get_display (drag);
+
+ atoms = gdk_content_formats_get_mime_types (gdk_drag_get_formats (drag), &n_atoms);
+ atomlist = g_new (Atom, n_atoms);
+ for (i = 0; i < n_atoms; i++)
+ atomlist[i] = gdk_x11_get_xatom_by_name_for_display (display, atoms[i]);
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_SURFACE_XID (drag_x11->ipc_surface),
+ gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
+ XA_ATOM, 32, PropModeReplace,
+ (guchar *)atomlist, n_atoms);
+
+ g_free (atomlist);
+
+ drag_x11->xdnd_targets_set = 1;
+}
+
+static void
+xdnd_set_actions (GdkX11Drag *drag_x11)
+{
+ GdkDrag *drag = GDK_DRAG (drag_x11);
+ Atom *atomlist;
+ gint i;
+ gint n_atoms;
+ guint actions;
+ GdkDisplay *display = gdk_drag_get_display (drag);
+
+ actions = gdk_drag_get_actions (drag);
+ n_atoms = 0;
+ for (i = 0; i < xdnd_n_actions; i++)
+ {
+ if (actions & xdnd_actions_table[i].action)
+ {
+ actions &= ~xdnd_actions_table[i].action;
+ n_atoms++;
+ }
+ }
+
+ atomlist = g_new (Atom, n_atoms);
+
+ actions = gdk_drag_get_actions (drag);
+ n_atoms = 0;
+ for (i = 0; i < xdnd_n_actions; i++)
+ {
+ if (actions & xdnd_actions_table[i].action)
+ {
+ actions &= ~xdnd_actions_table[i].action;
+ atomlist[n_atoms] = gdk_x11_get_xatom_by_name_for_display (display, xdnd_actions_table[i].name);
+ n_atoms++;
+ }
+ }
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_SURFACE_XID (drag_x11->ipc_surface),
+ gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
+ XA_ATOM, 32, PropModeReplace,
+ (guchar *)atomlist, n_atoms);
+
+ g_free (atomlist);
+
+ drag_x11->xdnd_actions = gdk_drag_get_actions (drag);
+}
+
+static void
+send_client_message_async_cb (Window window,
+ gboolean success,
+ gpointer data)
+{
+ GdkX11Drag *drag_x11 = data;
+ GdkDrag *drag = data;
+
+ GDK_DISPLAY_NOTE (gdk_drag_get_display (drag), DND,
+ g_message ("Got async callback for #%lx, success = %d",
+ window, success));
+
+ /* On failure, we immediately continue with the protocol
+ * so we don't end up blocking for a timeout
+ */
+ if (!success &&
+ window == drag_x11->proxy_xid)
+ {
+ drag_x11->proxy_xid = None;
+ gdk_drag_set_selected_action (drag, 0);
+ drag_x11->current_action = 0;
+ drag_x11->drag_status = GDK_DRAG_STATUS_DRAG;
+ }
+
+ g_object_unref (drag);
+}
+
+static void
+send_client_message_async (GdkDrag *drag,
+ Window window,
+ glong event_mask,
+ XClientMessageEvent *event_send)
+{
+ GdkDisplay *display = gdk_drag_get_display (drag);
+
+ g_object_ref (drag);
+
+ _gdk_x11_send_client_message_async (display, window,
+ FALSE, event_mask, event_send,
+ send_client_message_async_cb, drag);
+}
+
+static void
+xdnd_send_xevent (GdkX11Drag *drag_x11,
+ XEvent *event_send)
+{
+ GdkDrag *drag = GDK_DRAG (drag_x11);
+ GdkDisplay *display = gdk_drag_get_display (drag);
+ GdkSurface *surface;
+ glong event_mask;
+
+ g_assert (event_send->xany.type == ClientMessage);
+
+ /* We short-circuit messages to ourselves */
+ surface = gdk_x11_surface_lookup_for_display (display, drag_x11->proxy_xid);
+ if (surface)
+ {
+ if (gdk_x11_drop_filter (surface, event_send))
+ return;
+ }
+
+ if (_gdk_x11_display_is_root_window (display, drag_x11->proxy_xid))
+ event_mask = ButtonPressMask;
+ else
+ event_mask = 0;
+
+ send_client_message_async (drag, drag_x11->proxy_xid, event_mask,
+ &event_send->xclient);
+}
+
+static void
+xdnd_send_enter (GdkX11Drag *drag_x11)
+{
+ GdkDrag *drag = GDK_DRAG (drag_x11);
+ GdkDisplay *display = gdk_drag_get_display (drag);
+ const char * const *atoms;
+ gsize i, n_atoms;
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndEnter");
+ xev.xclient.format = 32;
+ xev.xclient.window = drag_x11->drop_xid
+ ? drag_x11->drop_xid
+ : drag_x11->proxy_xid;
+ xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
+ xev.xclient.data.l[1] = (drag_x11->version << 24); /* version */
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ GDK_DISPLAY_NOTE (display, DND,
+ g_message ("Sending enter source window %#lx XDND protocol version %d\n",
+ GDK_SURFACE_XID (drag_x11->ipc_surface), drag_x11->version));
+ atoms = gdk_content_formats_get_mime_types (gdk_drag_get_formats (drag), &n_atoms);
+
+ if (n_atoms > 3)
+ {
+ if (!drag_x11->xdnd_targets_set)
+ xdnd_set_targets (drag_x11);
+ xev.xclient.data.l[1] |= 1;
+ }
+ else
+ {
+ for (i = 0; i < n_atoms; i++)
+ {
+ xev.xclient.data.l[i + 2] = gdk_x11_atom_to_xatom_for_display (display, atoms[i]);
+ }
+ }
+
+ xdnd_send_xevent (drag_x11, &xev);
+}
+
+static void
+xdnd_send_leave (GdkX11Drag *drag_x11)
+{
+ GdkDrag *drag = GDK_DRAG (drag_x11);
+ GdkDisplay *display = gdk_drag_get_display (drag);
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndLeave");
+ xev.xclient.format = 32;
+ xev.xclient.window = drag_x11->drop_xid
+ ? drag_x11->drop_xid
+ : drag_x11->proxy_xid;
+ xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ xdnd_send_xevent (drag_x11, &xev);
+}
+
+static void
+xdnd_send_drop (GdkX11Drag *drag_x11,
+ guint32 time)
+{
+ GdkDrag *drag = GDK_DRAG (drag_x11);
+ GdkDisplay *display = gdk_drag_get_display (drag);
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndDrop");
+ xev.xclient.format = 32;
+ xev.xclient.window = drag_x11->drop_xid
+ ? drag_x11->drop_xid
+ : drag_x11->proxy_xid;
+ xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = time;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ xdnd_send_xevent (drag_x11, &xev);
+}
+
+static void
+xdnd_send_motion (GdkX11Drag *drag_x11,
+ gint x_root,
+ gint y_root,
+ GdkDragAction action,
+ guint32 time)
+{
+ GdkDrag *drag = GDK_DRAG (drag_x11);
+ GdkDisplay *display = gdk_drag_get_display (drag);
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndPosition");
+ xev.xclient.format = 32;
+ xev.xclient.window = drag_x11->drop_xid
+ ? drag_x11->drop_xid
+ : drag_x11->proxy_xid;
+ xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = (x_root << 16) | y_root;
+ xev.xclient.data.l[3] = time;
+ xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
+
+ xdnd_send_xevent (drag_x11, &xev);
+ drag_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
+}
+
+static guint32
+xdnd_check_dest (GdkDisplay *display,
+ Window win,
+ guint *xdnd_version)
+{
+ gboolean retval = FALSE;
+ Atom type = None;
+ int format;
+ unsigned long nitems, after;
+ guchar *data;
+ Atom *version;
+ Window *proxy_data;
+ Window proxy;
+ Atom xdnd_proxy_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndProxy");
+ Atom xdnd_aware_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndAware");
+
+ proxy = None;
+
+ gdk_x11_display_error_trap_push (display);
+ if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
+ xdnd_proxy_atom, 0,
+ 1, False, AnyPropertyType,
+ &type, &format, &nitems, &after,
+ &data) == Success)
+ {
+ if (type != None)
+ {
+ proxy_data = (Window *)data;
+
+ if ((format == 32) && (nitems == 1))
+ {
+ proxy = *proxy_data;
+ }
+ else
+ {
+ GDK_DISPLAY_NOTE (display, DND,
+ g_warning ("Invalid XdndProxy property on window %ld", win));
+ }
+
+ XFree (proxy_data);
+ }
+
+ if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
+ xdnd_aware_atom, 0,
+ 1, False, AnyPropertyType,
+ &type, &format, &nitems, &after,
+ &data) == Success) &&
+ type != None)
+ {
+ version = (Atom *)data;
+
+ if ((format == 32) && (nitems == 1))
+ {
+ if (*version >= 3)
+ retval = TRUE;
+ if (xdnd_version)
+ *xdnd_version = *version;
+ }
+ else
+ {
+ GDK_DISPLAY_NOTE (display, DND,
+ g_warning ("Invalid XdndAware property on window %ld", win));
+ }
+
+ XFree (version);
+ }
+ }
+
+ gdk_x11_display_error_trap_pop_ignored (display);
+
+ return retval ? (proxy ? proxy : win) : None;
+}
+
+/* Target side */
+
+static void
+base_precache_atoms (GdkDisplay *display)
+{
+ GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+
+ if (!display_x11->base_dnd_atoms_precached)
+ {
+ static const char *const precache_atoms[] = {
+ "WM_STATE",
+ "XdndAware",
+ "XdndProxy"
+ };
+
+ _gdk_x11_precache_atoms (display,
+ precache_atoms, G_N_ELEMENTS (precache_atoms));
+
+ display_x11->base_dnd_atoms_precached = TRUE;
+ }
+}
+
+static void
+xdnd_precache_atoms (GdkDisplay *display)
+{
+ GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+
+ if (!display_x11->xdnd_atoms_precached)
+ {
+ static const gchar *const precache_atoms[] = {
+ "XdndActionAsk",
+ "XdndActionCopy",
+ "XdndActionLink",
+ "XdndActionList",
+ "XdndActionMove",
+ "XdndActionPrivate",
+ "XdndDrop",
+ "XdndEnter",
+ "XdndFinished",
+ "XdndLeave",
+ "XdndPosition",
+ "XdndSelection",
+ "XdndStatus",
+ "XdndTypeList"
+ };
+
+ _gdk_x11_precache_atoms (display,
+ precache_atoms, G_N_ELEMENTS (precache_atoms));
+
+ display_x11->xdnd_atoms_precached = TRUE;
+ }
+}
+
+/* Source side */
+
+static void
+gdk_drag_do_leave (GdkX11Drag *drag_x11,
+ guint32 time)
+{
+ if (drag_x11->proxy_xid)
+ {
+ switch (drag_x11->protocol)
+ {
+ case GDK_DRAG_PROTO_XDND:
+ xdnd_send_leave (drag_x11);
+ break;
+ case GDK_DRAG_PROTO_ROOTWIN:
+ case GDK_DRAG_PROTO_NONE:
+ default:
+ break;
+ }
+
+ drag_x11->proxy_xid = None;
+ }
+}
+
+static GdkSurface *
+create_drag_surface (GdkDisplay *display)
+{
+ GdkSurface *surface;
+
+ surface = gdk_surface_new_popup (display, &(GdkRectangle) { 0, 0, 100, 100 });
+
+ gdk_surface_set_type_hint (surface, GDK_SURFACE_TYPE_HINT_DND);
+
+ return surface;
+}
+
+static Window
+_gdk_x11_display_get_drag_protocol (GdkDisplay *display,
+ Window xid,
+ GdkDragProtocol *protocol,
+ guint *version)
+
+{
+ GdkSurface *surface;
+ Window retval;
+
+ base_precache_atoms (display);
+
+ /* Check for a local drag */
+ surface = gdk_x11_surface_lookup_for_display (display, xid);
+ if (surface)
+ {
+ if (g_object_get_data (G_OBJECT (surface), "gdk-dnd-registered") != NULL)
+ {
+ *protocol = GDK_DRAG_PROTO_XDND;
+ *version = 5;
+ xdnd_precache_atoms (display);
+ GDK_DISPLAY_NOTE (display, DND, g_message ("Entering local Xdnd window %#x\n", (guint) xid));
+ return xid;
+ }
+ else if (_gdk_x11_display_is_root_window (display, xid))
+ {
+ *protocol = GDK_DRAG_PROTO_ROOTWIN;
+ GDK_DISPLAY_NOTE (display, DND, g_message ("Entering root window\n"));
+ return xid;
+ }
+ }
+ else if ((retval = xdnd_check_dest (display, xid, version)))
+ {
+ *protocol = GDK_DRAG_PROTO_XDND;
+ xdnd_precache_atoms (display);
+ GDK_DISPLAY_NOTE (display, DND, g_message ("Entering Xdnd window %#x\n", (guint) xid));
+ return retval;
+ }
+ else
+ {
+ /* Check if this is a root window */
+ gboolean rootwin = FALSE;
+
+ if (_gdk_x11_display_is_root_window (display, (Window) xid))
+ rootwin = TRUE;
+
+ if (rootwin)
+ {
+ GDK_DISPLAY_NOTE (display, DND, g_message ("Entering root window\n"));
+ *protocol = GDK_DRAG_PROTO_ROOTWIN;
+ return xid;
+ }
+ }
+
+ *protocol = GDK_DRAG_PROTO_NONE;
+
+ return 0; /* a.k.a. None */
+}
+
+static GdkSurfaceCache *
+drag_find_window_cache (GdkX11Drag *drag_x11,
+ GdkDisplay *display)
+{
+ if (!drag_x11->cache)
+ drag_x11->cache = gdk_surface_cache_get (display);
+
+ return drag_x11->cache;
+}
+
+static Window
+gdk_x11_drag_find_surface (GdkDrag *drag,
+ GdkSurface *drag_surface,
+ gint x_root,
+ gint y_root,
+ GdkDragProtocol *protocol)
+{
+ GdkX11Screen *screen_x11;
+ GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
+ GdkSurfaceCache *window_cache;
+ GdkDisplay *display;
+ Window dest;
+ Window proxy;
+
+ display = gdk_drag_get_display (drag);
+ screen_x11 = GDK_X11_SCREEN(GDK_X11_DISPLAY (display)->screen);
+
+ window_cache = drag_find_window_cache (drag_x11, display);
+
+ dest = get_client_window_at_coords (window_cache,
+ drag_surface && GDK_SURFACE_IS_X11 (drag_surface) ?
+ GDK_SURFACE_XID (drag_surface) : None,
+ x_root * screen_x11->surface_scale,
+ y_root * screen_x11->surface_scale);
+
+ if (drag_x11->dest_xid != dest)
+ {
+ drag_x11->dest_xid = dest;
+
+ /* Check if new destination accepts drags, and which protocol */
+
+ /* There is some ugliness here. We actually need to pass
+ * _three_ pieces of information to drag_motion - dest_surface,
+ * protocol, and the XID of the unproxied window. The first
+ * two are passed explicitly, the third implicitly through
+ * protocol->dest_xid.
+ */
+ proxy = _gdk_x11_display_get_drag_protocol (display,
+ dest,
+ protocol,
+ &drag_x11->version);
+ }
+ else
+ {
+ proxy = dest;
+ *protocol = drag_x11->protocol;
+ }
+
+ return proxy;
+}
+
+static void
+move_drag_surface (GdkDrag *drag,
+ guint x_root,
+ guint y_root)
+{
+ GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
+
+ gdk_surface_move (drag_x11->drag_surface,
+ x_root - drag_x11->hot_x,
+ y_root - drag_x11->hot_y);
+ gdk_surface_raise (drag_x11->drag_surface);
+}
+
+static gboolean
+gdk_x11_drag_drag_motion (GdkDrag *drag,
+ Window proxy_xid,
+ GdkDragProtocol protocol,
+ gint x_root,
+ gint y_root,
+ GdkDragAction suggested_action,
+ GdkDragAction possible_actions,
+ guint32 time)
+{
+ GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
+
+ if (drag_x11->drag_surface)
+ move_drag_surface (drag, x_root, y_root);
+
+ gdk_drag_set_actions (drag, possible_actions);
+
+ if (protocol == GDK_DRAG_PROTO_XDND && drag_x11->version == 0)
+ {
+ /* This ugly hack is necessary since GTK+ doesn't know about
+ * the XDND protocol version, and in particular doesn't know
+ * that gdk_drag_find_window() has the side-effect
+ * of setting drag_x11->version, and therefore sometimes call
+ * gdk_x11_drag_drag_motion() without a prior call to
+ * gdk_drag_find_window(). This happens, e.g.
+ * when GTK+ is proxying DND events to embedded windows.
+ */
+ if (proxy_xid)
+ {
+ GdkDisplay *display = gdk_drag_get_display (drag);
+
+ xdnd_check_dest (display,
+ proxy_xid,
+ &drag_x11->version);
+ }
+ }
+
+ /* When we have a Xdnd target, make sure our XdndActionList
+ * matches the current actions;
+ */
+ if (protocol == GDK_DRAG_PROTO_XDND && drag_x11->xdnd_actions != gdk_drag_get_actions (drag))
+ {
+ if (proxy_xid)
+ {
+ GdkDisplay *display = gdk_drag_get_display (drag);
+ GdkDrop *drop = GDK_X11_DISPLAY (display)->current_drop;
+
+ if (drop && GDK_SURFACE_XID (gdk_drop_get_surface (drop)) == proxy_xid)
+ gdk_x11_drop_read_actions (drop);
+ else
+ xdnd_set_actions (drag_x11);
+ }
+ }
+
+ if (drag_x11->proxy_xid != proxy_xid)
+ {
+ /* Send a leave to the last destination */
+ gdk_drag_do_leave (drag_x11, time);
+ drag_x11->drag_status = GDK_DRAG_STATUS_DRAG;
+
+ /* Check if new destination accepts drags, and which protocol */
+
+ if (proxy_xid)
+ {
+ drag_x11->proxy_xid = proxy_xid;
+ drag_x11->drop_xid = drag_x11->dest_xid;
+ drag_x11->protocol = protocol;
+
+ switch (protocol)
+ {
+ case GDK_DRAG_PROTO_XDND:
+ xdnd_send_enter (drag_x11);
+ break;
+
+ case GDK_DRAG_PROTO_ROOTWIN:
+ case GDK_DRAG_PROTO_NONE:
+ default:
+ break;
+ }
+ }
+ else
+ {
+ drag_x11->proxy_xid = None;
+ drag_x11->drop_xid = None;
+ gdk_drag_set_selected_action (drag, 0);
+ }
+
+ /* Push a status event, to let the client know that
+ * the drag changed
+ */
+ drag_x11->current_action = gdk_drag_get_selected_action (drag);
+ }
+
+ /* Send a drag-motion event */
+
+ drag_x11->last_x = x_root;
+ drag_x11->last_y = y_root;
+
+ if (drag_x11->proxy_xid)
+ {
+ GdkDisplay *display = gdk_drag_get_display (drag);
+ GdkX11Screen *screen_x11 = GDK_X11_SCREEN(GDK_X11_DISPLAY (display)->screen);
+
+ if (drag_x11->drag_status == GDK_DRAG_STATUS_DRAG)
+ {
+ switch (drag_x11->protocol)
+ {
+ case GDK_DRAG_PROTO_XDND:
+ xdnd_send_motion (drag_x11, x_root * screen_x11->surface_scale, y_root * screen_x11->surface_scale, suggested_action, time);
+ break;
+
+ case GDK_DRAG_PROTO_ROOTWIN:
+ {
+ GdkContentFormats *formats = gdk_drag_get_formats (drag);
+ /* GTK+ traditionally has used application/x-rootwin-drop,
+ * but the XDND spec specifies x-rootwindow-drop.
+ */
+ if (gdk_content_formats_contain_mime_type (formats, "application/x-rootwindow-drop") ||
+ gdk_content_formats_contain_mime_type (formats, "application/x-rootwin-drop"))
+ gdk_drag_set_selected_action (drag, suggested_action);
+ else
+ gdk_drag_set_selected_action (drag, 0);
+
+ drag_x11->current_action = gdk_drag_get_selected_action (drag);
+ }
+ break;
+ case GDK_DRAG_PROTO_NONE:
+ g_warning ("Invalid drag protocol %u in gdk_x11_drag_drag_motion()", drag_x11->protocol);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gdk_x11_drag_drop (GdkDrag *drag,
+ guint32 time)
+{
+ GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
+
+ if (drag_x11->proxy_xid)
+ {
+ switch (drag_x11->protocol)
+ {
+ case GDK_DRAG_PROTO_XDND:
+ xdnd_send_drop (drag_x11, time);
+ break;
+
+ case GDK_DRAG_PROTO_ROOTWIN:
+ g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
+ break;
+ case GDK_DRAG_PROTO_NONE:
+ g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
+ break;
+ default:
+ g_warning ("Drag protocol %u is not valid in gdk_drag_drop()", drag_x11->protocol);
+ break;
+ }
+ }
+}
+
+/* Destination side */
+
+void
+_gdk_x11_surface_register_dnd (GdkSurface *surface)
+{
+ static const gulong xdnd_version = 5;
+ GdkDisplay *display = gdk_surface_get_display (surface);
+
+ g_return_if_fail (surface != NULL);
+
+ base_precache_atoms (display);
+
+ if (g_object_get_data (G_OBJECT (surface), "gdk-dnd-registered") != NULL)
+ return;
+ else
+ g_object_set_data (G_OBJECT (surface), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
+
+ /* Set XdndAware */
+
+ /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_SURFACE_XID (surface),
+ gdk_x11_get_xatom_by_name_for_display (display, "XdndAware"),
+ XA_ATOM, 32, PropModeReplace,
+ (guchar *)&xdnd_version, 1);
+}
+
+static GdkSurface *
+gdk_x11_drag_get_drag_surface (GdkDrag *drag)
+{
+ return GDK_X11_DRAG (drag)->drag_surface;
+}
+
+static void
+gdk_x11_drag_set_hotspot (GdkDrag *drag,
+ gint hot_x,
+ gint hot_y)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+
+ x11_drag->hot_x = hot_x;
+ x11_drag->hot_y = hot_y;
+
+ if (x11_drag->grab_seat)
+ {
+ /* DnD is managed, update current position */
+ move_drag_surface (drag, x11_drag->last_x, x11_drag->last_y);
+ }
+}
+
+static void
+gdk_x11_drag_default_output_done (GObject *drag,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (!gdk_drag_write_finish (GDK_DRAG (drag), result, &error))
+ {
+ GDK_DISPLAY_NOTE (gdk_drag_get_display (GDK_DRAG (drag)), DND, g_printerr ("failed to write stream: %s\n", error->message));
+ g_error_free (error);
+ }
+}
+
+static void
+gdk_x11_drag_default_output_handler (GOutputStream *stream,
+ const char *mime_type,
+ gpointer user_data)
+{
+ gdk_drag_write_async (GDK_DRAG (user_data),
+ mime_type,
+ stream,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ gdk_x11_drag_default_output_done,
+ NULL);
+ g_object_unref (stream);
+}
+
+static gboolean
+gdk_x11_drag_xevent (GdkDisplay *display,
+ const XEvent *xevent,
+ gpointer data)
+{
+ GdkDrag *drag = GDK_DRAG (data);
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+ Window xwindow;
+ Atom xselection;
+
+ xwindow = GDK_SURFACE_XID (x11_drag->ipc_surface);
+ xselection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
+
+ if (xevent->xany.window != xwindow)
+ return FALSE;
+
+ switch (xevent->type)
+ {
+ case SelectionClear:
+ if (xevent->xselectionclear.selection != xselection)
+ return FALSE;
+
+ if (xevent->xselectionclear.time < x11_drag->timestamp)
+ {
+ GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("ignoring SelectionClear with too old timestamp (%lu vs %lu)\n",
+ xevent->xselectionclear.time, x11_drag->timestamp));
+ return FALSE;
+ }
+
+ GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("got SelectionClear, aborting DND\n"));
+ gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR);
+ return TRUE;
+
+ case SelectionRequest:
+ {
+ const char *target, *property;
+
+ if (xevent->xselectionrequest.selection != xselection)
+ return FALSE;
+
+ target = gdk_x11_get_xatom_name_for_display (display, xevent->xselectionrequest.target);
+ if (xevent->xselectionrequest.property == None)
+ property = target;
+ else
+ property = gdk_x11_get_xatom_name_for_display (display, xevent->xselectionrequest.property);
+
+ if (xevent->xselectionrequest.requestor == None)
+ {
+ GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("got SelectionRequest for %s @ %s with NULL window, ignoring\n",
+ target, property));
+ return TRUE;
+ }
+
+ GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("got SelectionRequest for %s @ %s\n",
+ target, property));
+
+ gdk_x11_selection_output_streams_create (display,
+ gdk_drag_get_formats (drag),
+ xevent->xselectionrequest.requestor,
+ xevent->xselectionrequest.selection,
+ xevent->xselectionrequest.target,
+ xevent->xselectionrequest.property ? xevent->xselectionrequest.property
+ : xevent->xselectionrequest.target,
+ xevent->xselectionrequest.time,
+ gdk_x11_drag_default_output_handler,
+ drag);
+ return TRUE;
+ }
+
+ case ClientMessage:
+ if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus"))
+ gdk_x11_drag_handle_status (display, xevent);
+ else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished"))
+ gdk_x11_drag_handle_finished (display, xevent);
+ else
+ return FALSE;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static double
+ease_out_cubic (double t)
+{
+ double p = t - 1;
+ return p * p * p + 1;
+}
+
+
+#define ANIM_TIME 500000 /* half a second */
+
+typedef struct _GdkDragAnim GdkDragAnim;
+struct _GdkDragAnim {
+ GdkX11Drag *drag;
+ GdkFrameClock *frame_clock;
+ gint64 start_time;
+};
+
+static void
+gdk_drag_anim_destroy (GdkDragAnim *anim)
+{
+ g_object_unref (anim->drag);
+ g_slice_free (GdkDragAnim, anim);
+}
+
+static gboolean
+gdk_drag_anim_timeout (gpointer data)
+{
+ GdkDragAnim *anim = data;
+ GdkX11Drag *drag = anim->drag;
+ GdkFrameClock *frame_clock = anim->frame_clock;
+ gint64 current_time;
+ double f;
+ double t;
+
+ if (!frame_clock)
+ return G_SOURCE_REMOVE;
+
+ current_time = gdk_frame_clock_get_frame_time (frame_clock);
+
+ f = (current_time - anim->start_time) / (double) ANIM_TIME;
+
+ if (f >= 1.0)
+ return G_SOURCE_REMOVE;
+
+ t = ease_out_cubic (f);
+
+ gdk_surface_show (drag->drag_surface);
+ gdk_surface_move (drag->drag_surface,
+ drag->last_x + (drag->start_x - drag->last_x) * t,
+ drag->last_y + (drag->start_y - drag->last_y) * t);
+ gdk_surface_set_opacity (drag->drag_surface, 1.0 - f);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+gdk_x11_drag_release_selection (GdkDrag *drag)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+ GdkDisplay *display;
+ Display *xdisplay;
+ Window xwindow;
+ Atom xselection;
+
+ display = gdk_drag_get_display (drag);
+ xdisplay = GDK_DISPLAY_XDISPLAY (display);
+ xselection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
+ xwindow = GDK_SURFACE_XID (x11_drag->ipc_surface);
+
+ if (XGetSelectionOwner (xdisplay, xselection) == xwindow)
+ XSetSelectionOwner (xdisplay, xselection, None, CurrentTime);
+}
+
+static void
+gdk_x11_drag_drop_done (GdkDrag *drag,
+ gboolean success)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+ GdkDragAnim *anim;
+/*
+ cairo_surface_t *win_surface;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+*/
+ guint id;
+
+ gdk_x11_drag_release_selection (drag);
+
+ g_signal_handlers_disconnect_by_func (gdk_drag_get_display (drag),
+ gdk_x11_drag_xevent,
+ drag);
+ if (success)
+ {
+ gdk_surface_hide (x11_drag->drag_surface);
+ return;
+ }
+
+/*
+ win_surface = _gdk_surface_ref_cairo_surface (x11_drag->drag_surface);
+ surface = gdk_surface_create_similar_surface (x11_drag->drag_surface,
+ cairo_surface_get_content (win_surface),
+ gdk_surface_get_width (x11_drag->drag_surface),
+ gdk_surface_get_height (x11_drag->drag_surface));
+ cr = cairo_create (surface);
+ cairo_set_source_surface (cr, win_surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+ cairo_surface_destroy (win_surface);
+
+ pattern = cairo_pattern_create_for_surface (surface);
+
+ gdk_surface_set_background_pattern (x11_drag->drag_surface, pattern);
+
+ cairo_pattern_destroy (pattern);
+ cairo_surface_destroy (surface);
+*/
+
+ anim = g_slice_new0 (GdkDragAnim);
+ anim->drag = g_object_ref (x11_drag);
+ anim->frame_clock = gdk_surface_get_frame_clock (x11_drag->drag_surface);
+ anim->start_time = gdk_frame_clock_get_frame_time (anim->frame_clock);
+
+ id = g_timeout_add_full (G_PRIORITY_DEFAULT, 17,
+ gdk_drag_anim_timeout, anim,
+ (GDestroyNotify) gdk_drag_anim_destroy);
+ g_source_set_name_by_id (id, "[gtk+] gdk_drag_anim_timeout");
+}
+
+static gboolean
+drag_grab (GdkDrag *drag)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+ GdkDevice *device = gdk_drag_get_device (drag);
+ GdkSeatCapabilities capabilities;
+ GdkDisplay *display;
+ Window root;
+ GdkSeat *seat;
+ gint keycode, i;
+ GdkCursor *cursor;
+
+ if (!x11_drag->ipc_surface)
+ return FALSE;
+
+ display = gdk_drag_get_display (drag);
+ root = GDK_DISPLAY_XROOTWIN (display);
+ seat = gdk_device_get_seat (gdk_drag_get_device (drag));
+
+#ifdef XINPUT_2
+ if (GDK_IS_X11_DEVICE_XI2 (device))
+ capabilities = GDK_SEAT_CAPABILITY_ALL_POINTING;
+ else
+#endif
+ capabilities = GDK_SEAT_CAPABILITY_ALL;
+
+ cursor = gdk_drag_get_cursor (drag, x11_drag->current_action);
+ g_set_object (&x11_drag->cursor, cursor);
+
+ if (gdk_seat_grab (seat, x11_drag->ipc_surface,
+ capabilities, FALSE,
+ x11_drag->cursor, NULL, NULL, NULL) != GDK_GRAB_SUCCESS)
+ return FALSE;
+
+ g_set_object (&x11_drag->grab_seat, seat);
+
+ gdk_x11_display_error_trap_push (display);
+
+ for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
+ {
+ keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
+ grab_keys[i].keysym);
+ if (keycode == NoSymbol)
+ continue;
+
+#ifdef XINPUT_2
+ if (GDK_IS_X11_DEVICE_XI2 (device))
+ {
+ gint deviceid = gdk_x11_device_get_id (gdk_seat_get_keyboard (seat));
+ unsigned char mask[XIMaskLen(XI_LASTEVENT)];
+ XIGrabModifiers mods;
+ XIEventMask evmask;
+ gint num_mods;
+
+ memset (mask, 0, sizeof (mask));
+ XISetMask (mask, XI_KeyPress);
+ XISetMask (mask, XI_KeyRelease);
+
+ evmask.deviceid = deviceid;
+ evmask.mask_len = sizeof (mask);
+ evmask.mask = mask;
+
+ num_mods = 1;
+ mods.modifiers = grab_keys[i].modifiers;
+
+ XIGrabKeycode (GDK_DISPLAY_XDISPLAY (display),
+ deviceid,
+ keycode,
+ root,
+ GrabModeAsync,
+ GrabModeAsync,
+ False,
+ &evmask,
+ num_mods,
+ &mods);
+ }
+ else
+#endif
+ {
+ XGrabKey (GDK_DISPLAY_XDISPLAY (display),
+ keycode, grab_keys[i].modifiers,
+ root,
+ FALSE,
+ GrabModeAsync,
+ GrabModeAsync);
+ }
+ }
+
+ gdk_x11_display_error_trap_pop_ignored (display);
+
+ return TRUE;
+}
+
+static void
+drag_ungrab (GdkDrag *drag)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+ GdkDisplay *display;
+ GdkDevice *keyboard;
+ Window root;
+ gint keycode, i;
+
+ if (!x11_drag->grab_seat)
+ return;
+
+ gdk_seat_ungrab (x11_drag->grab_seat);
+
+ display = gdk_drag_get_display (drag);
+ keyboard = gdk_seat_get_keyboard (x11_drag->grab_seat);
+ root = GDK_DISPLAY_XROOTWIN (display);
+ g_clear_object (&x11_drag->grab_seat);
+
+ for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
+ {
+ keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
+ grab_keys[i].keysym);
+ if (keycode == NoSymbol)
+ continue;
+
+#ifdef XINPUT_2
+ if (GDK_IS_X11_DEVICE_XI2 (keyboard))
+ {
+ XIGrabModifiers mods;
+ gint num_mods;
+
+ num_mods = 1;
+ mods.modifiers = grab_keys[i].modifiers;
+
+ XIUngrabKeycode (GDK_DISPLAY_XDISPLAY (display),
+ gdk_x11_device_get_id (keyboard),
+ keycode,
+ root,
+ num_mods,
+ &mods);
+ }
+ else
+#endif /* XINPUT_2 */
+ {
+ XUngrabKey (GDK_DISPLAY_XDISPLAY (display),
+ keycode, grab_keys[i].modifiers,
+ root);
+ }
+ }
+}
+
+GdkDrag *
+_gdk_x11_surface_drag_begin (GdkSurface *surface,
+ GdkDevice *device,
+ GdkContentProvider *content,
+ GdkDragAction actions,
+ gint dx,
+ gint dy)
+{
+ GdkX11Drag *x11_drag;
+ GdkDrag *drag;
+ GdkDisplay *display;
+ int x_root, y_root;
+ Atom xselection;
+ GdkSurface *ipc_surface;
+
+ display = gdk_surface_get_display (surface);
+
+ ipc_surface = gdk_surface_new_popup (display, &(GdkRectangle) { -99, -99, 1, 1 });
+
+ drag = (GdkDrag *) g_object_new (GDK_TYPE_X11_DRAG,
+ "device", device,
+ "content", content,
+ "surface", ipc_surface,
+ NULL);
+ x11_drag = GDK_X11_DRAG (drag);
+
+ g_signal_connect (display, "xevent", G_CALLBACK (gdk_x11_drag_xevent), drag);
+
+ precache_target_list (drag);
+
+ gdk_device_get_position (device, &x_root, &y_root);
+ x_root += dx;
+ y_root += dy;
+
+ x11_drag->start_x = x_root;
+ x11_drag->start_y = y_root;
+ x11_drag->last_x = x_root;
+ x11_drag->last_y = y_root;
+
+ x11_drag->protocol = GDK_DRAG_PROTO_XDND;
+ x11_drag->actions = actions;
+ x11_drag->ipc_surface = ipc_surface;
+ if (gdk_surface_get_group (surface))
+ gdk_surface_set_group (x11_drag->ipc_surface, surface);
+ gdk_surface_show (x11_drag->ipc_surface);
+
+ x11_drag->drag_surface = create_drag_surface (display);
+
+ if (!drag_grab (drag))
+ {
+ g_object_unref (drag);
+ return NULL;
+ }
+
+ move_drag_surface (drag, x_root, y_root);
+
+ x11_drag->timestamp = gdk_display_get_last_seen_time (display);
+ xselection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
+ XSetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
+ xselection,
+ GDK_SURFACE_XID (x11_drag->ipc_surface),
+ x11_drag->timestamp);
+ if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), xselection) != GDK_SURFACE_XID (x11_drag->ipc_surface))
+ {
+ GDK_DISPLAY_NOTE (display, DND, g_printerr ("failed XSetSelectionOwner() on \"XdndSelection\", aborting DND\n"));
+ g_object_unref (drag);
+ return NULL;
+ }
+
+ return drag;
+}
+
+static void
+gdk_x11_drag_set_cursor (GdkDrag *drag,
+ GdkCursor *cursor)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+
+ if (!g_set_object (&x11_drag->cursor, cursor))
+ return;
+
+ if (x11_drag->grab_seat)
+ {
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ gdk_device_grab (gdk_seat_get_pointer (x11_drag->grab_seat),
+ x11_drag->ipc_surface,
+ GDK_OWNERSHIP_APPLICATION, FALSE,
+ GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+ cursor, GDK_CURRENT_TIME);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+ }
+}
+
+static void
+gdk_x11_drag_cancel (GdkDrag *drag,
+ GdkDragCancelReason reason)
+{
+ drag_ungrab (drag);
+ gdk_drag_drop_done (drag, FALSE);
+}
+
+static void
+gdk_x11_drag_drop_performed (GdkDrag *drag,
+ guint32 time_)
+{
+ gdk_x11_drag_drop (drag, time_);
+ drag_ungrab (drag);
+}
+
+#define BIG_STEP 20
+#define SMALL_STEP 1
+
+static void
+gdk_drag_get_current_actions (GdkModifierType state,
+ gint button,
+ GdkDragAction actions,
+ GdkDragAction *suggested_action,
+ GdkDragAction *possible_actions)
+{
+ *suggested_action = 0;
+ *possible_actions = 0;
+
+ if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
+ {
+ *suggested_action = GDK_ACTION_ASK;
+ *possible_actions = actions;
+ }
+ else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
+ {
+ if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
+ {
+ if (actions & GDK_ACTION_LINK)
+ {
+ *suggested_action = GDK_ACTION_LINK;
+ *possible_actions = GDK_ACTION_LINK;
+ }
+ }
+ else if (state & GDK_CONTROL_MASK)
+ {
+ if (actions & GDK_ACTION_COPY)
+ {
+ *suggested_action = GDK_ACTION_COPY;
+ *possible_actions = GDK_ACTION_COPY;
+ }
+ }
+ else
+ {
+ if (actions & GDK_ACTION_MOVE)
+ {
+ *suggested_action = GDK_ACTION_MOVE;
+ *possible_actions = GDK_ACTION_MOVE;
+ }
+ }
+ }
+ else
+ {
+ *possible_actions = actions;
+
+ if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
+ *suggested_action = GDK_ACTION_ASK;
+ else if (actions & GDK_ACTION_COPY)
+ *suggested_action = GDK_ACTION_COPY;
+ else if (actions & GDK_ACTION_MOVE)
+ *suggested_action = GDK_ACTION_MOVE;
+ else if (actions & GDK_ACTION_LINK)
+ *suggested_action = GDK_ACTION_LINK;
+ }
+}
+
+static void
+gdk_drag_update (GdkDrag *drag,
+ gdouble x_root,
+ gdouble y_root,
+ GdkModifierType mods,
+ guint32 evtime)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+ GdkDragAction suggested_action;
+ GdkDragAction possible_actions;
+ GdkDragProtocol protocol;
+ Window proxy;
+
+ gdk_drag_get_current_actions (mods, GDK_BUTTON_PRIMARY, x11_drag->actions,
+ &suggested_action, &possible_actions);
+
+ proxy = gdk_x11_drag_find_surface (drag,
+ x11_drag->drag_surface,
+ x_root, y_root, &protocol);
+
+ gdk_x11_drag_drag_motion (drag, proxy, protocol, x_root, y_root,
+ suggested_action, possible_actions, evtime);
+}
+
+static gboolean
+gdk_dnd_handle_motion_event (GdkDrag *drag,
+ const GdkEventMotion *event)
+{
+ GdkModifierType state;
+
+ if (!gdk_event_get_state ((GdkEvent *) event, &state))
+ return FALSE;
+
+ gdk_drag_update (drag, event->x_root, event->y_root, state,
+ gdk_event_get_time ((GdkEvent *) event));
+ return TRUE;
+}
+
+static gboolean
+gdk_dnd_handle_key_event (GdkDrag *drag,
+ const GdkEventKey *event)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+ GdkModifierType state;
+ GdkDevice *pointer;
+ gint dx, dy;
+
+ dx = dy = 0;
+ state = event->state;
+ pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
+
+ if (event->any.type == GDK_KEY_PRESS)
+ {
+ switch (event->keyval)
+ {
+ case GDK_KEY_Escape:
+ gdk_drag_cancel (drag, GDK_DRAG_CANCEL_USER_CANCELLED);
+ return TRUE;
+
+ case GDK_KEY_space:
+ case GDK_KEY_Return:
+ case GDK_KEY_ISO_Enter:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_KP_Space:
+ if ((gdk_drag_get_selected_action (drag) != 0) &&
+ (x11_drag->proxy_xid != None))
+ {
+ g_signal_emit_by_name (drag, "drop-performed");
+ }
+ else
+ gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
+
+ return TRUE;
+
+ case GDK_KEY_Up:
+ case GDK_KEY_KP_Up:
+ dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
+ break;
+
+ case GDK_KEY_Down:
+ case GDK_KEY_KP_Down:
+ dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
+ break;
+
+ case GDK_KEY_Left:
+ case GDK_KEY_KP_Left:
+ dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
+ break;
+
+ case GDK_KEY_Right:
+ case GDK_KEY_KP_Right:
+ dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* The state is not yet updated in the event, so we need
+ * to query it here. We could use XGetModifierMapping, but
+ * that would be overkill.
+ */
+ _gdk_device_query_state (pointer, NULL, NULL, NULL, NULL, NULL, NULL, &state);
+
+ if (dx != 0 || dy != 0)
+ {
+ x11_drag->last_x += dx;
+ x11_drag->last_y += dy;
+ gdk_device_warp (pointer, x11_drag->last_x, x11_drag->last_y);
+ }
+
+ gdk_drag_update (drag, x11_drag->last_x, x11_drag->last_y, state,
+ gdk_event_get_time ((GdkEvent *) event));
+
+ return TRUE;
+}
+
+static gboolean
+gdk_dnd_handle_grab_broken_event (GdkDrag *drag,
+ const GdkEventGrabBroken *event)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+
+ /* Don't cancel if we break the implicit grab from the initial button_press.
+ * Also, don't cancel if we re-grab on the widget or on our IPC window, for
+ * example, when changing the drag cursor.
+ */
+ if (event->implicit ||
+ event->grab_surface == x11_drag->drag_surface ||
+ event->grab_surface == x11_drag->ipc_surface)
+ return FALSE;
+
+ if (gdk_event_get_device ((GdkEvent *) event) !=
+ gdk_drag_get_device (drag))
+ return FALSE;
+
+ gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR);
+ return TRUE;
+}
+
+static gboolean
+gdk_dnd_handle_button_event (GdkDrag *drag,
+ const GdkEventButton *event)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+
+#if 0
+ /* FIXME: Check the button matches */
+ if (event->button != x11_drag->button)
+ return FALSE;
+#endif
+
+ if ((gdk_drag_get_selected_action (drag) != 0) &&
+ (x11_drag->proxy_xid != None))
+ {
+ g_signal_emit_by_name (drag, "drop-performed");
+ }
+ else
+ gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
+
+ return TRUE;
+}
+
+gboolean
+gdk_x11_drag_handle_event (GdkDrag *drag,
+ const GdkEvent *event)
+{
+ GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
+
+ if (!x11_drag->grab_seat)
+ return FALSE;
+
+ switch ((guint) event->any.type)
+ {
+ case GDK_MOTION_NOTIFY:
+ return gdk_dnd_handle_motion_event (drag, &event->motion);
+ case GDK_BUTTON_RELEASE:
+ return gdk_dnd_handle_button_event (drag, &event->button);
+ case GDK_KEY_PRESS:
+ case GDK_KEY_RELEASE:
+ return gdk_dnd_handle_key_event (drag, &event->key);
+ case GDK_GRAB_BROKEN:
+ return gdk_dnd_handle_grab_broken_event (drag, &event->grab_broken);
+ default:
+ break;
+ }
+
+ return FALSE;
+}
'gdkdevicemanager-x11.c',
'gdkdevicemanager-xi2.c',
'gdkdisplay-x11.c',
- 'gdkdnd-x11.c',
+ 'gdkdrag-x11.c',
'gdkdrop-x11.c',
'gdkeventsource.c',
'gdkeventtranslator.c',